Agent Skills › forcedotcom/sf-skills

forcedotcom/sf-skills

GitHub

通过MCP工具执行三步流水线生成Salesforce Flow元数据XML,支持Screen、Autolaunched、Record-Triggered及Scheduled类型流程。适用于自动化业务逻辑、数据处理及触发器构建,严禁手动生成或跳过步骤。

89 skills 661

Install All Skills

npx skills add forcedotcom/sf-skills --all -g -y
More Options

List skills in collection

npx skills add forcedotcom/sf-skills --list

Skills in Collection (89)

通过MCP工具执行三步流水线生成Salesforce Flow元数据XML,支持Screen、Autolaunched、Record-Triggered及Scheduled类型流程。适用于自动化业务逻辑、数据处理及触发器构建,严禁手动生成或跳过步骤。
创建Salesforce Flow 生成Flow元数据XML 自动化业务流程 当记录创建时触发 每日定时任务 发送电子邮件条件 更新字段条件 工作流配置
skills/automation-flow-generate/SKILL.md
npx skills add forcedotcom/sf-skills --skill automation-flow-generate -g -y
SKILL.md
Frontmatter
{
    "name": "automation-flow-generate",
    "metadata": {
        "version": "1.0"
    },
    "description": "Generate Salesforce Flows using the MCP tool execute_metadata_action. Use when the user asks to create, build, or generate a flow — including Screen, Autolaunched, Record-Triggered (before\/after-save), Scheduled. Also trigger for flow-like requests such as \"when a record is created\", \"trigger daily at\", \"send an email when\", \"update the field when\", \"automate\", \"workflow\", or \"flow XML\/metadata\". This is the only skill for Salesforce Flow generation."
}

Goal

Generate Salesforce Flow metadata by running the required 3-step MCP pipeline (fetchGroundedObjectMetadata → flowElementSelection → flowElementGeneration) and return the flow XML.

When to Use This Skill

Use this skill when you need to:

  • Create any type of Flow (Screen, Autolaunched, Record-Triggered, Scheduled)
  • Generate Flow metadata XML
  • Automate business processes without code
  • Build user-guided workflows or background automation
  • Troubleshoot deployment errors related to Flows

Specification

Flow Metadata Specification

Overview

Salesforce Flows are powerful automation tools that enable complex business process automation without code. Flows can collect and process data through interactive screens, execute logic and calculations, manipulate records, call external services, and trigger based on various events. Flow types include Screen Flows (user-guided), Autolaunched Flows (background processing), Record-Triggered Flows (database events) and Scheduled Flows (time-based).

Purpose

  • Automate complex business processes with declarative logic and branching
  • Guide users through multi-step data collection and decision workflows via Screen Flows
  • Perform CRUD operations on Salesforce records automatically
  • Execute background processing and integrations via Autolaunched Flows
  • React to record changes in real-time with Record-Triggered Flows
  • Schedule recurring tasks and batch operations with Scheduled Flows
  • Create reusable, maintainable automation that admins can modify without code

Flow Generation Pipeline

MANDATORY: You MUST follow this exact 3-step pipeline. No exceptions. No shortcuts. No skipping steps. Do NOT manually create flow metadata XML or attempt to generate flow metadata outside of this pipeline. Do NOT attempt to use any other tool, API, or method to generate flow metadata. This pipeline is the ONLY supported way to generate flows. Any deviation will produce invalid or broken metadata.

MCP Connection Details

All 3 pipeline steps MUST be called using this MCP tool:

  • MCP Tool Name: execute_metadata_action
  • The action parameter selects which pipeline step to run: "fetchGroundedObjectMetadata", "flowElementSelection", or "flowElementGeneration"

Flow generation is a strict 3-step pipeline. ALL steps must be called in order. Every step is required. There is no alternative approach — this is the only way to generate flow metadata:

Step 1 (REQUIRED): Fetch Grounded Object Metadata (fetchGroundedObjectMetadata)

Fetches org schema metadata relevant to the flow generation request. This step is mandatory and must always be called first.

Inputs (all required):

  • userPrompt (STRING, REQUIRED): The user's natural language request
  • inflightMetadata (ARRAY, REQUIRED): Custom objects/fields from local sfdx project. Use empty array [] if none needed.

Outputs:

  • groundingMetadata (STRING): Grounded object metadata for org schema relevant to the request, returned as a JSON string. You must pass this directly to Step 2 — it is already a string and does not need to be serialized again.

Step 2 (REQUIRED): Flow Element Selection (flowElementSelection)

Selects flow elements (assignments, decisions, record ops, etc.) and their connections based on the user prompt and grounded metadata. This step is mandatory and must be called after Step 1.

Inputs (all required):

  • userPrompt (STRING, REQUIRED): The user's natural language request (must be the same value as Step 1)
  • groundingMetadata (STRING, REQUIRED): Org schema metadata (must be the exact string returned from Step 1 output — pass it directly, do NOT serialize it again)
  • operationId (STRING, REQUIRED): Operation ID (use empty string "" for first call)

Outputs:

  • operationId (STRING): Operation ID. You must pass this to Step 3.
  • userOutput (STRING): Reasoning for next steps. You can show this to the user.

Step 3 (REQUIRED): Flow Element Generation (flowElementGeneration)

Generates flow metadata element by element. This step is mandatory and must be called after Step 2. Must be called repeatedly in a loop until isComplete is true.

Inputs (all required):

  • operationId (STRING, REQUIRED): Operation ID from Step 2 output
  • requestSource (STRING, REQUIRED): The source of the request. Use "A4V" to get flow metadata in XML format.

Outputs:

  • isComplete (BOOLEAN): Indicates if the flow generation is complete. You must check this value.
  • result (STRING): Result of the flow element generation. Contains the final flow metadata only when isComplete is true.

MANDATORY: Loop until complete. NEVER pause or ask the user to confirm continuation.

  • A flow can have any number of elements (10, 15, or more). Each call generates one element at a time, so you may need many iterations. This is expected and normal.
  • Call flowElementGeneration with the operationId from Step 2 and requestSource (use "A4V" for XML output, empty string or other value for JSON).
  • Check the isComplete output and the result field after each call.
  • 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.
  • 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.
  • When isComplete is true, extract the flow metadata from the result field.
  • If errors are returned, stop the loop and surface the error to the user.

STRICT CONSTRAINTS (CRITICAL) — These rules apply to the XML returned by the generation pipeline:

  • DO NOT modify the content, values, or child nodes inside any block.
  • DO NOT add new nodes, tags, attributes, or text (do not add missing labels, X/Y coordinates, etc.).
  • DO NOT remove any existing nodes.

inflightMetadata Format

DATA TYPE: ARRAY (not string)

STRICT NAMING CONVENTION - MUST FOLLOW EXACTLY:

Property Correct Name Do NOT Use
Object API name apiName objectApiName, name, objectName
Field API name apiName fieldApiName, name, fieldName
Field type type fieldType, dataType
Lookup target referenceTo relatedTo, lookupTo, reference

When custom objects are needed (sample format showing multiple field data types):

[
  {
    "type": "CustomObject",
    "apiName": "CustomerRequest__c",
    "label": "Customer Request",
    "fields": [
      {
        "apiName": "Status__c",
        "type": "Picklist",
        "label": "Status",
        "values": ["New", "In Progress", "Completed"]
      },
      {
        "apiName": "Priority__c",
        "type": "Number",
        "label": "Priority"
      },
      {
        "apiName": "AssignedTo__c",
        "type": "Lookup",
        "label": "Assigned To",
        "referenceTo": "User"
      },
      {
        "apiName": "Description__c",
        "type": "Textarea",
        "label": "Description"
      },
      {
        "apiName": "Email__c",
        "type": "Email",
        "label": "Contact Email"
      },
      {
        "apiName": "DueDate__c",
        "type": "Date",
        "label": "Due Date"
      },
      {
        "apiName": "IsUrgent__c",
        "type": "Boolean",
        "label": "Is Urgent"
      },
      {
        "apiName": "Amount__c",
        "type": "Currency",
        "label": "Amount"
      }
    ],
    "relationships": []
  }
]

Supported field types: Text, Textarea, Number, Picklist, Lookup, Email, Phone, URL, Date, Datetime, Boolean, Checkbox, Currency, Percent

When no custom objects needed:

[]

MANDATORY Decision Logic for inflightMetadata (DATA TYPE: ARRAY)

  1. REQUIRED - First: Scan the local sfdx project for custom objects and fields that are relevant to the user's flow request.
  2. If relevant custom objects ARE found: You MUST extract and pass them as an array of structured objects (see format above)
  3. If NO relevant custom objects found: You MUST pass an empty array [] (NOT the string "[]")
  4. NEVER: Pass text descriptions, instructions, or string representations in inflightMetadata
  5. MANDATORY: The data type MUST be ARRAY, not STRING

Instructions for Vibes when custom objects ARE relevant:

  • Extract the object metadata and map to JSON properties:
    • apiName: The object's API name (with __c suffix for custom objects)
    • label: The object's display label
    • type: Set to "CustomObject"
    • fields: Array of field objects, each containing:
      • apiName: The field's API name (with __c suffix for custom fields)
      • type: The field type (Text, Number, Picklist, Lookup, etc.)
      • label: The field's display label
      • values: (Picklist only) Array of picklist values
      • referenceTo: (Lookup only) The target object API name
  • Include only objects and fields that are relevant to the flow being generated

Mandatory Enhancement Rules

  • userPrompt: REQUIRED.
    • If the user requests a single flow: use the user's prompt as-is.
    • If the user requests multiple flows: you MUST split the request and write a separate, focused userPrompt for each individual flow. Each userPrompt must describe only ONE flow. Do NOT pass the entire multi-flow request as a single userPrompt. See the multiple flows section below for examples.
  • inflightMetadata: REQUIRED. Always use ARRAY data type.
    • MUST use [] (empty array) when no custom objects needed
    • MUST use structured array of objects when custom objects are relevant
    • NEVER use string "[]" - this is incorrect
    • NEVER use text descriptions - only structured object metadata

MANDATORY: Multiple Flows = Multiple Separate Pipelines

FIRST: Before calling any pipeline step, check if the user's request contains multiple flows. If it does, you MUST split it into separate single-flow prompts. Each flow gets its own 3-step pipeline with its own userPrompt that describes ONLY that one flow.

NEVER pass a multi-flow request as a single userPrompt field. NEVER club multiple flow descriptions into one userPrompt.

When the user requests multiple flows (e.g., "Create flows for my app: 1) ... 2) ... 3) ..."), you MUST:

  1. Split the request into separate individual flow descriptions.
  2. Run a separate 3-step pipeline for each flow, using a userPrompt that describes ONLY that one flow.
  3. Execute ALL pipelines SEQUENTIALLY — one after another, NEVER in parallel. Do NOT stop after the first flow. Do NOT wait for the user to ask you to continue. Do NOT summarize and stop. Keep going until every requested flow has been fully generated.

WRONG - Multiple flows clubbed into one userPrompt:

{
  "userPrompt": "Create flows for the app: 1) Record-Triggered Flow on ResourceAllocation__c to update Resource__c. 2) Screen Flow to allocate resources. 3) Record-Triggered Flow on Supply__c to auto-flag Low_Stock__c.",
  ...
}

CORRECT - Separate call for EACH flow:

Flow 1 - Step 1 (fetchGroundedObjectMetadata):

{
  "userPrompt": "Create a Screen Flow named Tenant_Onboarding that captures tenant details, selects a Unit__c with Status__c = 'Vacant', creates Lease__c...",
  "inflightMetadata": [...]
}

Then call Step 2 (flowElementSelection) with the groundingMetadata from Step 1, then Step 3 (flowElementGeneration) with the operationId from Step 2.

Flow 2 - Step 1 (fetchGroundedObjectMetadata):

{
  "userPrompt": "Create an Autolaunched Flow named Generate_Onboarding_Checklist that given a Lease__c Id input, queries OnboardingTask__c...",
  "inflightMetadata": [...]
}

Then call Step 2 and Step 3 for this flow.

Flow 3 - Step 1 (fetchGroundedObjectMetadata):

{
  "userPrompt": "Create a Record-Triggered Flow named Sync_Unit_On_Lease_Changes that on insert and update of Lease__c...",
  "inflightMetadata": [...]
}

Then call Step 2 and Step 3 for this flow.

Mandatory Rules:

  • If there are N flows to generate, there MUST be N separate 3-step pipelines and ALL N pipelines MUST be executed. No exceptions. Do NOT stop after generating only one flow.
  • You MUST fully complete the current flow's 3-step pipeline (including looping Step 3 until isComplete is true or errors are returned) BEFORE starting the next flow's pipeline. Do NOT interleave or parallelize pipelines across flows. Everything is SEQUENTIAL — NEVER parallel.
  • After completing a flow's pipeline, immediately start the next flow's pipeline. Do NOT pause, summarize, or wait for user confirmation between flows.
  • For each flow, you MUST scan the local sfdx project to populate inflightMetadata with custom objects/fields specific to that flow prompt.
  • Each flow pipeline MUST have its own inflightMetadata containing only the objects/fields relevant to that particular flow.

Example Tool Calls

Example 1: Standard objects only (no custom objects)

Step 1 - fetchGroundedObjectMetadata:

{
  "userPrompt": "Create a scheduled-triggered Flow named Daily_Good_Morning that runs daily at 6:00 AM and sends an email to the running user saying good morning.",
  "inflightMetadata": []
}

Step 2 - flowElementSelection:

{
  "userPrompt": "Create a scheduled-triggered Flow named Daily_Good_Morning that runs daily at 6:00 AM and sends an email to the running user saying good morning.",
  "groundingMetadata": "<groundingMetadata string from Step 1 — pass directly, do not serialize again>",
  "operationId": ""
}

Step 3 - flowElementGeneration (call in a loop):

{
  "operationId": "<operationId from Step 2>",
  "requestSource": "A4V"
}

Call repeatedly with the same operationId until isComplete is true or errors are returned. A flow can have any number of elements, so expect multiple iterations. When isComplete is true, extract the flow metadata from the result field. Use "requestSource": "A4V" to get flow metadata in XML format.

Example 2: With custom objects from local sfdx project

Step 1 - fetchGroundedObjectMetadata:

{
  "userPrompt": "Create a flow that updates the status of a Customer Request when it's assigned",
  "inflightMetadata": [
    {
      "type": "CustomObject",
      "apiName": "CustomerRequest__c",
      "label": "Customer Request",
      "fields": [
        {
          "apiName": "Status__c",
          "type": "Picklist",
          "label": "Status",
          "values": ["New", "In Progress", "Completed"]
        },
        {
          "apiName": "AssignedTo__c",
          "type": "Lookup",
          "label": "Assigned To",
          "referenceTo": "User"
        }
      ],
      "relationships": []
    }
  ]
}

Step 2 - flowElementSelection:

{
  "userPrompt": "Create a flow that updates the status of a Customer Request when it's assigned",
  "groundingMetadata": "<groundingMetadata string from Step 1 — pass directly, do not serialize again>",
  "operationId": ""
}

Step 3 - flowElementGeneration (call in a loop):

{
  "operationId": "<operationId from Step 2>",
  "requestSource": "A4V"
}

Call repeatedly with the same operationId until isComplete is true or errors are returned. A flow can have any number of elements, so expect multiple iterations. When isComplete is true, extract the flow metadata from the result field. Use "requestSource": "A4V" to get flow metadata in XML format.

Mandatory Best Practices

  • ALWAYS follow the 3-step pipeline: fetchGroundedObjectMetadata → flowElementSelection → flowElementGeneration. This is the ONLY way to generate flow metadata. There are no alternatives.
  • Do NOT manually create flow metadata XML, JSON, or any other format outside of this pipeline.
  • When the user explicitly requests fixes to validation or deployment errors in an already-generated flow XML, you ARE permitted to make targeted manual edits to the XML to resolve those errors. This is the only exception to the "no manual metadata" rule.
  • Do NOT attempt to "optimize" by skipping steps or combining steps. Each step is atomic and required.
  • NEVER skip any step in the pipeline. All 3 steps are required.
  • NEVER try to generate flow metadata without calling all 3 steps.
  • NEVER deviate from this pipeline under any circumstance — even if you think you know the flow structure.
  • For single flow requests: you MUST use the user prompt as userPrompt.
  • For multiple flow requests: you MUST run a separate 3-step pipeline for each flow SEQUENTIALLY (one after another, NEVER in parallel), and you MUST execute ALL of them — do NOT stop after the first flow.
  • You MUST put flow requirements in userPrompt, NOT in inflightMetadata.
  • inflightMetadata is ONLY for custom object/field metadata from local project (see above). No exceptions.
  • Step 3 MUST be called in a loop with the same operationId from Step 2 until isComplete is true or errors are returned. A flow can have any number of elements — do NOT stop early, do NOT pause to ask the user if they want to continue, regardless of how many iterations it takes.
  • You MUST only extract the flow metadata from the result field when isComplete is true.

CRITICAL Verification Checklist (MUST VERIFY BEFORE AND AFTER EVERY FLOW GENERATION)

Failure to follow this checklist exactly will result in broken or missing flow metadata.

  • Pipeline: ALL 3 steps are called in strict order (fetchGroundedObjectMetadata → flowElementSelection → flowElementGeneration). No step is skipped.
  • No manual metadata: Flow metadata is NOT manually created, modified, or generated outside of this pipeline by any means
  • No deviation: No alternative tools, APIs, or methods were used instead of or alongside this pipeline
  • userPrompt contains a single flow prompt. If user requested multiple flows, the request was split and each pipeline received a separate userPrompt describing only one flow
  • userPrompt is passed consistently to both Step 1 and Step 2 (same value)
  • inflightMetadata is ARRAY data type (NOT string)
  • inflightMetadata is [] when no custom objects needed
  • inflightMetadata contains structured objects extracted by scanning the local sfdx project for relevant custom objects/fields
  • inflightMetadata does NOT contain "[]" (string) - must be [] (array)
  • inflightMetadata does NOT contain text descriptions or instructions
  • groundingMetadata from Step 1 output is passed directly to Step 2 input (it is already a string — do NOT serialize it again)
  • operationId from Step 2 output is passed to Step 3 input
  • requestSource should be set to "A4V" always
  • Step 3 is called in a loop with the same operationId from Step 2 until isComplete is true or errors are returned — no pausing, no asking the user to continue, no matter how many iterations
  • Multi-flow: Each flow's full pipeline is completed before starting the next flow's pipeline (no interleaving)
  • result field is used to extract the XML flow metadata only when isComplete is true
  • No additions to XML: NO elements, attributes, or properties were added that were not present in the original pipeline output. Nothing was inserted (no <label>, <description>, or any other node). The final XML must be identical to what the pipeline returned.
  • Error fix exception: If the user explicitly requested fixes to validation/deployment errors, targeted manual edits to the XML are permitted and the "No additions to XML" / "No manual metadata" constraints do not apply to those edits.
将Salesforce B2B Commerce开源组件集成到商店,使其在Experience Builder中可用。需先解释命令意图,自动解析包目录、克隆或验证仓库,并检查商店元数据。
integrate open code components open source B2B commerce add open code components forcedotcom/b2b-commerce-open-source-components
skills/commerce-b2b-open-code-components-integrate/SKILL.md
npx skills add forcedotcom/sf-skills --skill commerce-b2b-open-code-components-integrate -g -y
SKILL.md
Frontmatter
{
    "name": "commerce-b2b-open-code-components-integrate",
    "metadata": {
        "version": "1.0"
    },
    "description": "Integrate Salesforce B2B Commerce open source components from GitHub into B2B Commerce stores. Use when users mention \"integrate open code components\", \"open source B2B commerce\", \"add open code components\", \"forcedotcom\/b2b-commerce-open-source-components\", or want to add open source commerce components to their store. Copies all components and labels so they become available in Experience Builder.",
    "allowed-tools": "Bash(git clone:*) Bash(cp:*) Read"
}

When to Use This Skill

Use this skill when you need to:

  • Integrate all open source B2B Commerce components into a store
  • Add open source components to a new or existing B2B Commerce store
  • Make open code components available in Experience Builder

Rules

  1. Always explain before executing. Before running any command, you MUST tell the user what the command does and why you are running it. Never just show a raw command and ask for permission. The user should be able to read your explanation and understand the purpose before approving.

Overview

This skill copies all open source B2B Commerce components from the official Salesforce repository (https://github.com/forcedotcom/b2b-commerce-open-source-components) into a B2B Commerce store's site metadata. After integration, the components appear in the Experience Builder component palette.


Startup Flow

When this skill is triggered, perform these checks automatically before copying.

Check 0: Resolve Package Directory

Read sfdx-project.json and pick the active package directory. Extract packageDirectories[] and use the entry with "default": true; if no entry is flagged default, use the first entry. Use this value as <package-dir> everywhere below. If sfdx-project.json is missing or has no packageDirectories, tell the user and abort.

Check 1: Open Source Repository

Verify the repo is cloned at .tmp/b2b-commerce-open-source-components:

  1. If directory does not exist: Tell user: "I'm cloning the official B2B Commerce open source components repository from GitHub into a local .tmp/ folder. This gives us access to all the open code components." Then run: git clone https://github.com/forcedotcom/b2b-commerce-open-source-components .tmp/b2b-commerce-open-source-components
  2. If directory exists and contains force-app/main/default/sfdc_cms__lwc and sfdc_cms__label, present options:

    "Open source repository is already cloned. How would you like to proceed?"

    1. Reuse existing — Use the already cloned repository
    2. Re-clone — Remove and clone fresh from GitHub
  3. If directory exists but structure is invalid: Tell user: "The cloned repository has an unexpected structure. I'll remove it and clone a fresh copy." Then remove and re-clone.
  4. If clone fails: inform user and abort

Check 2: Store and Site Metadata

Verify a store is selected and site metadata is available locally:

  1. Tell user: "I'm checking if your project already has B2B store metadata locally." Check if <package-dir>/main/default/digitalExperiences/site/ contains any store directories.
  2. If store metadata exists: use it. If multiple stores found, ask user to select one.
  3. If no store metadata found: Try retrieving from the connected org before delegating:
    1. Run sf org list (or check sf config get target-org) to find a connected org. Ask the user to confirm or pick one if more than one.
    2. List DigitalExperienceBundle site bundles in that org with sf org list metadata --metadata-type DigitalExperienceBundle --target-org <alias>. Filter to site/* entries.
    3. If at least one site bundle exists, ask the user which to use, then run: sf project retrieve start --metadata "DigitalExperienceBundle:site/<storeName>" --target-org <alias> The bundle lands at <package-dir>/main/default/digitalExperiences/site/<storeName>/.
    4. Only if no connected org is available, or no site bundles are found, or retrieve fails: delegate to the commerce-b2b-store-create skill.

Required state after all checks:

  • Package dir — the value resolved in Check 0 (e.g., force-app)
  • Store name — the selected fullName value (e.g., My_B2B_Store1)
  • Site metadata path<package-dir>/main/default/digitalExperiences/site/<store-name>/
  • Repo path.tmp/b2b-commerce-open-source-components/

Integration Task

Copy all components and labels from cloned repo to site directory:

  • Source: .tmp/b2b-commerce-open-source-components/force-app/main/default/sfdc_cms__lwc/* and sfdc_cms__label/* (the open source repo's own layout — always force-app)
  • Destination: <package-dir>/main/default/digitalExperiences/site/<store-name>/sfdc_cms__lwc/ and sfdc_cms__label/ (<package-dir> resolved in Check 0)

Steps:

  1. Tell user: "I'm checking if open code components already exist in your store's site metadata." Check if destination directories already contain files.
  2. If files exist, present options:

    "Components already exist in {store-name}. How would you like to proceed?"

    1. Overwrite all — Replace all existing components with latest from repo
    2. Copy only new — Skip existing components, copy only ones not yet present
  3. Tell user: "I'm now copying all open code LWC components from the cloned repository into your store's site metadata directory." Copy all component directories from source to destination.
  4. Tell user: "I'm copying the associated label files that these components need." Copy all label directories from source to destination.
  5. Report: "Copied X components and Y label sets"

Output:

✅ Integration Complete!

Copied: X components and Y label sets to <store-name>

Next Steps:
1. Deploy: sf project deploy start -d <package-dir>/main/default/digitalExperiences/site/<store-name>
2. Open Experience Builder and use new components from the palette
3. Publish your site when ready

Example Interaction

User: "Integrate open code components to my store"

Agent: "I'm checking if the open source components repository is already cloned locally..."

Agent: (repo exists)

"Open source repository is already cloned. How would you like to proceed?"

  1. Reuse existing — Use the already cloned repository
  2. Re-clone — Remove and clone fresh from GitHub

User: "1"

Agent: "I'm checking if your project already has B2B store metadata locally..."

  • ✓ Found store metadata for My_B2B_Store1

Agent: "I'm checking if open code components already exist in your store's site metadata..."

Agent: (files exist)

"Components already exist in My_B2B_Store1. How would you like to proceed?"

  1. Overwrite all — Replace all existing components with latest from repo
  2. Copy only new — Skip existing components, copy only ones not yet present

User: "1"

Agent: "I'm now copying all open code LWC components from the cloned repository into your store's site metadata directory..." Agent: "I'm copying the associated label files that these components need..."

  • ✓ Copied 45 components and 38 label sets
✅ Integration Complete!

Copied: 45 components and 38 label sets to My_B2B_Store1

Next Steps:
1. Deploy: sf project deploy start -d force-app/main/default/digitalExperiences/site/My_B2B_Store1
2. Open Experience Builder and use new components from the palette
3. Publish your site when ready

Error Handling

Error Message Action
Store not found "Store '{name}' not found in org." List stores again
Git clone failed "Failed to clone repository. Check internet connection." Retry or abort
Invalid repo structure "Repository structure has changed. Expected sfdc_cms__lwc and sfdc_cms__label." Warn user, abort
File copy failed "Failed to copy files. Check file permissions." Show error details

Verification Checklist

  • Startup Flow completed: repo cloned, store metadata available
  • Components copied to correct destination path (sfdc_cms__lwc/)
  • Labels copied to correct destination path (sfdc_cms__label/)
  • No file permission errors during copy
  • Deployment command provided and user informed about testing
用于在Salesforce中交互式创建B2B Commerce Store并自动获取前端Storefront元数据。需先创建后端Store,严禁手动生成元数据,通过CLI命令列出站点供用户选择确认。
创建B2B Commerce商店 构建Commerce前端 设置B2B商店 检索Commerce元数据 部署B2B前端
skills/commerce-b2b-store-create/SKILL.md
npx skills add forcedotcom/sf-skills --skill commerce-b2b-store-create -g -y
SKILL.md
Frontmatter
{
    "name": "commerce-b2b-store-create",
    "metadata": {
        "version": "1.0",
        "category": "commerce"
    },
    "description": "Interactive workflow to create Commerce B2B Stores and retrieve storefront metadata. Use when users want to: create B2B Commerce stores, build Commerce storefronts, set up B2B stores from Vibes, retrieve Commerce metadata, deploy Commerce experiences, work with DigitalExperienceBundle for Commerce.",
    "compatibility": "Requires Commerce licenses, Experience Cloud, Salesforce CLI"
}

Commerce B2B Storefront Creation

Interactive workflow to create a Commerce B2B Store in Salesforce and retrieve the auto-generated storefront metadata to your repository.

Critical Concepts

Commerce B2B = Store (backend data) + Storefront (frontend metadata). Store must be created first in the org to auto-generate the Storefront. Never create storefront metadata manually.

See Store vs Storefront Reference

When to Use This Skill

Trigger when users request:

  • "Create a B2B Commerce store"
  • "Build a Commerce storefront"
  • "Set up Commerce B2B"
  • "Create B2B Commerce"
  • "Retrieve Commerce storefront metadata"
  • "Deploy B2B storefront"

Rules That Always Apply

  1. Always follow the interactive flow. Do NOT skip steps. Each step requires user confirmation before proceeding.

  2. Never create storefront metadata manually. The Commerce setup wizard generates hundreds of configuration values. Manual creation will fail.

  3. Always list sites before retrieval. Store names get underscores and number suffixes (e.g., "My B2B Store" → "My_B2B_Store1"). Let the user select from the actual list.

  4. Always use --json flag. Include --json on all Salesforce CLI commands for parseable output.

Interactive Workflow: 7 Steps

Step 1: Explain Commerce B2B Concept

Agent explains: Commerce has Store (data) + Storefront (metadata). Store must be created first.

See: Store vs Storefront Reference


Step 2: Guide User to Create B2B Store

Agent provides these steps:

  1. Navigate to Setup → Commerce → Stores

    • Or: App Launcher → Commerce → Create Store
  2. Click "Create Store" or "Setup New Store"

  3. Select "Commerce Store" as the store type

  4. Follow the wizard:

    • Store Name: Choose descriptive name (e.g., "My B2B Store")
      • Important: Spaces become underscores in folder names
    • Site URL: Unique URL name for the site
  5. Complete wizard - it creates:

    • WebStore record
    • Default buyer group and entitlement policies
    • Associated Digital Experience (LWR site)
  6. Optional: Configure payment gateway, tax provider, shipping

Agent then asks: "Have you completed creating the B2B Store in your org? Reply 'yes' when ready and provide the store name you used."


Step 3: Get User Confirmation

Agent waits for: User confirmation and store name

Agent validates: Store name format (no special characters, spaces will appear as underscores)

Agent acknowledges: "Great! Let me list the available storefronts in your org..."


Step 4: List Available LWR Sites

Agent executes:

sf org list metadata --metadata-type DigitalExperienceConfig --json

Agent should:

  • Parse JSON output to extract site names
  • Display as numbered list
  • Explain naming (underscores, number suffixes)

Example output:

Available Digital Experience sites:
1. My_B2B_Store1
2. Partner_Portal
3. Customer_Community

Step 5: Let User Select Storefront

Agent asks: "Which site corresponds to your B2B Store? Select the site name:"

Agent validates: Selection matches available sites

Agent confirms: "Got it! I'll retrieve metadata for [site-name]..."


Step 6: Retrieve Storefront Metadata

Agent executes:

sf project retrieve start -m DigitalExperienceBundle:site/<selected-store-name> --json

Agent should:

  • Show retrieval progress
  • Confirm successful retrieval
  • List retrieved directory structure

Expected output:

Retrieved: force-app/main/default/digitalExperiences/site/My_B2B_Store1/
├── My_B2B_Store1.digitalExperience-meta.xml
├── sfdc_cms__view/ (home, current_cart, detail_*, list_*, etc.)
├── sfdc_cms__site/
├── sfdc_cms__route/
└── [other sfdc_cms__* directories]

Step 7: Provide Next Steps

Agent provides:

Metadata retrieved successfully!

Next steps:

  • Customize with custom LWCs or branding changes
  • Deploy: sf project deploy start --source-dir force-app/main/default/digitalExperiences/site/My_B2B_Store1/ --json

Resources: DigitalExperienceBundle Docs, B2B Commerce Guide


Reference

  • store-vs-storefront.md - Technical details on Store vs Storefront, source control, and why manual creation fails

Remember

Store first (creates storefront) → Retrieve → Customize

负责 Salesforce Data Cloud 的 Act 阶段,管理激活、目标及数据动作等下游交付工作。用于将受众推送至外部系统,不包含分段创建或查询。
管理激活和激活目标 配置数据动作及下游交付
skills/data360-activate/SKILL.md
npx skills add forcedotcom/sf-skills --skill data360-activate -g -y
SKILL.md
Frontmatter
{
    "name": "data360-activate",
    "metadata": {
        "version": "1.0"
    },
    "description": "Salesforce Data Cloud Act phase. Use this skill when the user manages activations, activation targets, data actions, or downstream delivery of Data Cloud audiences and data. TRIGGER when: user manages activations, activation targets, data actions, or downstream delivery of Data Cloud audiences and data. DO NOT TRIGGER when: the task is segment creation (use data360-segment), data retrieval\/search work (use data360-query), or STDM\/session tracing (use agentforce-observe).",
    "compatibility": "Requires an external community sf data360 CLI plugin and a Data Cloud-enabled org"
}

data360-activate: Data Cloud Act Phase

Use this skill when the user needs downstream delivery work: activations, activation targets, data actions, or pushing Data Cloud outputs into other systems.

When This Skill Owns the Task

Use data360-activate when the work involves:

  • sf data360 activation *
  • sf data360 activation-target *
  • sf data360 data-action *
  • sf data360 data-action-target *
  • verifying downstream delivery setup

Delegate elsewhere when the user is:


Required Context to Gather First

Ask for or infer:

  • target org alias
  • destination platform or downstream system
  • whether the segment already exists and is published
  • whether the user needs create, inspect, update, or delete
  • whether the task is activation-focused or data-action-focused

Core Operating Rules

  • Verify the upstream segment or insight is healthy before creating downstream delivery assets.
  • Run the shared readiness classifier before mutating activation assets: node ../data360-orchestrate/scripts/diagnose-org.mjs -o <org> --phase act --json.
  • Inspect available platforms and targets before mutating activation setup.
  • Keep destination definitions deterministic and reusable where possible.
  • Treat downstream credential and platform constraints as separate validation concerns.
  • Prefer read-only inspection first when the destination state is unclear.

Recommended Workflow

1. Classify readiness for act work

node ../data360-orchestrate/scripts/diagnose-org.mjs -o <org> --phase act --json

2. Inspect destinations first

sf data360 activation platforms -o <org> 2>/dev/null
sf data360 activation-target list -o <org> 2>/dev/null
sf data360 data-action-target list -o <org> 2>/dev/null

3. Create the destination before the activation

sf data360 activation-target create -o <org> -f target.json 2>/dev/null
sf data360 data-action-target create -o <org> -f target.json 2>/dev/null

4. Create the activation or data action

sf data360 activation create -o <org> -f activation.json 2>/dev/null
sf data360 data-action create -o <org> -f action.json 2>/dev/null

5. Verify downstream readiness

sf data360 activation list -o <org> 2>/dev/null
sf data360 activation data -o <org> --name <activation> 2>/dev/null

High-Signal Gotchas

  • Activation design depends on a healthy published upstream segment.
  • Destination configuration usually comes before activation creation.
  • Downstream credential and platform constraints may live outside the Data Cloud CLI alone.
  • Read-only inspection is the safest first move when the destination setup is unclear.
  • CdpActivationTarget or CdpActivationExternalPlatform means the activation surface is gated for the current org/user; guide the user toward activation setup, permissions, and destination configuration instead of retrying blindly.

Output Format

Act task: <activation / activation-target / data-action / data-action-target>
Destination: <platform or target>
Target org: <alias>
Artifacts: <definition files / commands>
Verification: <listed / created / blocked>
Next step: <destination validation or downstream testing>

References

提供Salesforce Data Cloud Python代码扩展的完整开发工作流,包括初始化项目、本地测试、权限扫描及部署。支持脚本和函数两种类型,需验证SF CLI、Python 3.11及Docker等前置条件。
创建新的代码扩展项目 在本地测试代码扩展 扫描代码所需权限 将代码扩展部署到Data Cloud 处理Data Cloud转换逻辑
skills/data360-code-extension-generate/SKILL.md
npx skills add forcedotcom/sf-skills --skill data360-code-extension-generate -g -y
SKILL.md
Frontmatter
{
    "name": "data360-code-extension-generate",
    "metadata": {
        "version": "1.0"
    },
    "description": "Develop and deploy Data Cloud Code Extensions using SF CLI plugin. Use this skill when creating custom Python transformations for Data Cloud, deploying code extensions, or testing data transformations. Supports init, run, scan, and deploy operations."
}

data360-code-extension-generate Skill

Overview

This skill provides a complete workflow for developing, testing, and deploying custom Python code extensions to Salesforce Data Cloud. Code extensions allow you to write Python transformations that read from and write to Data Lake Objects (DLOs) and Data Model Objects (DMOs).

When to Use

  • User wants to create a new code extension project
  • User needs to test a code extension locally
  • User wants to scan code for required permissions
  • User needs to deploy a code extension to Data Cloud
  • User is working with Data Cloud transformations
  • User wants to read/write DLO or DMO data programmatically

Prerequisites Check

Before executing any code extension commands, verify prerequisites:

  1. SF CLI with plugin installed

    sf plugins --core | grep data-code-extension
    

    If not installed:

    sf plugins install @salesforce/plugin-data-codeextension
    
  2. Python 3.11

    python --version  # Should show 3.11.x
    
  3. Data Cloud Custom Code SDK

    pip list | grep salesforce-data-customcode
    

    If not installed:

    pip install salesforce-data-customcode
    
  4. Docker running (for deploy only)

    docker ps
    
  5. Authenticated org

    sf org display --target-org <org_alias> --json
    

Skill Workflow

Phase 1: Initialize Project

Create a new code extension project with scaffolding.

Commands:

For script-based code extensions (batch transformations):

sf data-code-extension script init --package-dir <directory>

For function-based code extensions (real-time):

sf data-code-extension function init --package-dir <directory>

Required Option:

  • --package-dir, -p - Directory path where the package will be created

What it creates:

my-transform/              # Project root
├── payload/               # CRITICAL: This is what --package-dir must point to for deploy
│   ├── entrypoint.py      # Main transformation code
│   └── config.json        # Code extension configuration
├── requirements.txt       # Python dependencies
└── README.md

Directory Context During Workflow

IMPORTANT: Understanding the directory structure is critical for successful deployment.

Commands and their directory requirements:

Command Run From Path/File Argument
init Parent directory <project-name> or .
scan Project root ./payload/entrypoint.py
run Project root ./payload/entrypoint.py
deploy Project root --package-dir ./payload (REQUIRED)

CRITICAL: The --package-dir argument in deploy command MUST point to the payload directory, not the project root.

Phase 2: Develop Transformation

Edit payload/entrypoint.py with transformation logic.

Script Example (Batch):

from datacustomcode import Client

client = Client()

# Read from DLO
df = client.read_dlo('Employee__dll')

# Transform data (uppercase position field)
df['position_upper'] = df['position'].str.upper()

# Write to output DLO
client.write_to_dlo('Employee_Upper__dll', df, 'overwrite')

Function Example (Real-time):

from datacustomcode import FunctionClient

def transform(event, context):
    client = FunctionClient(context)
    input_data = event['data']
    output = {
        'name': input_data['name'].upper(),
        'status': 'processed'
    }
    return output

Common Operations:

  • client.read_dlo('DLO_Name__dll') - Read from DLO
  • client.read_dmo('DMO_Name') - Read from DMO
  • client.write_to_dlo('DLO_Name__dll', df, 'overwrite') - Write to DLO
  • client.write_to_dmo('DMO_Name', df, 'upsert') - Write to DMO

Phase 3: Scan for Permissions

Scan the entrypoint file to detect required permissions and generate config.json.

Command:

sf data-code-extension script scan --entrypoint ./payload/entrypoint.py

What it detects:

  • Read permissions for DLOs/DMOs
  • Write permissions for DLOs/DMOs
  • Python package dependencies
  • Updates config.json and requirements.txt

Phase 4: Validate DLO Schema (Pre-Test Check)

CRITICAL: Before running tests locally, validate that all DLOs used in your code exist and have the expected fields.

Step 4a: Extract DLOs from config.json

After scanning, review the generated config.json to identify all DLOs:

cat payload/config.json

Step 4b: Validate Each DLO Schema

Use the data360-schema-get skill to verify DLOs exist and check field names.

For each DLO referenced in your code:

  1. Verify DLO exists:

    python3 scripts/get_dlo_schema.py <org_alias> <dlo_name>
    
  2. Verify field names match — compare fields used in your entrypoint.py against the DLO schema.

  3. Check all DLOs:

    • Validate all DLOs in read permissions
    • Validate all DLOs in write permissions
    • Check field names match exactly (case-sensitive)
    • Verify data types are compatible with operations

Step 4c: Validation Checklist

Before proceeding to run, ensure:

  • All DLOs in config.json exist in target org
  • All field names used in code exist in DLO schemas
  • Field data types match your transformation logic
  • Primary key fields are correctly identified
  • Write target DLOs are created and accessible

Phase 5: Test Locally

After validating DLO schemas, run the code extension locally against your Data Cloud org.

Command:

sf data-code-extension script run --entrypoint <entrypoint_file> --target-org <org_alias> [options]

Options:

  • --target-org, -o - SF CLI org alias (required)
  • --config-file, -c - Custom config file path

If you get errors:

  • Re-validate DLO schemas
  • Check field names are exact matches
  • Verify data types are compatible
  • Review error messages for field/DLO issues

Phase 6: Deploy to Data Cloud

Deploy the code extension to Data Cloud for scheduled or on-demand execution.

CRITICAL: You MUST specify --package-dir ./payload to point to the payload directory created by init.

Command:

sf data-code-extension script deploy --target-org <org_alias> --name <name> --package-dir ./payload --package-version <version> --description <description> [options]

Required Options:

  • --target-org, -o - SF CLI org alias
  • --name, -n - Name for code extension deployment
  • --package-dir - Path to payload directory (REQUIRED - must be ./payload when running from project root)
  • --package-version - Version string (default: 0.0.1)
  • --description - Description of code extension

Optional Options:

  • --cpu-size - CPU size: CPU_L, CPU_XL, CPU_2XL (default), CPU_4XL
  • --function-invoke-opt - Function invoke options (for function type)
  • --network - Docker network (default: default)

After deployment:

  • Navigate to Data Cloud in Salesforce UI
  • Go to Data Transforms section
  • Find your deployment by name
  • Click "Run Now" to execute
  • Schedule for recurring execution

Error Handling

Common Issues and Solutions

Error Solution
command data-code-extension not found sf plugins install @salesforce/plugin-data-codeextension
datacustomcode CLI not found pip install salesforce-data-customcode
Python version mismatch Use pyenv: pyenv install 3.11.0 && pyenv local 3.11.0
Cannot connect to Docker daemon Start Docker Desktop
No org found for alias sf org login web --alias <org_alias>
config.json not found sf data-code-extension script scan --entrypoint ./payload/entrypoint.py
DLO not found Verify DLO exists (use data360-schema-get skill), check spelling and __dll suffix
Permission denied writing Re-run scan, verify target DLO exists and is writable
Deploy fails - wrong directory Ensure --package-dir points to payload/ directory, not project root

Best Practices

Development

  1. Always scan before testing — run scan after code changes
  2. Test locally first — use run command before deploying
  3. Use version control — git commit after each successful test
  4. Version your deployments — use semantic versioning (1.0.0, 1.1.0, etc.)
  5. Deploy from project root with --package-dir ./payload

Performance

  • CPU_L: Small datasets (< 1M records)
  • CPU_2XL: Medium datasets (1M-10M records)
  • CPU_4XL: Large datasets (> 10M records)

Security

  1. No hardcoded credentials — use SF CLI authentication only
  2. Validate input data — check for nulls and data types
  3. Limit write permissions — only grant necessary DLO/DMO access

Integration with Other Skills

Use with data360-schema-get skill (CRITICAL for validation):

The data360-schema-get skill is required for validating DLOs before testing code extensions.

Use with Datakit Workflow:

  1. Create DLO via code extension
  2. Map DLO to DMO using datakit workflow
  3. Use DMO in segments and activations

Command Reference

Command Purpose Required Args
script init Create new script project --package-dir
function init Create new function project --package-dir
script scan Generate config entrypoint file
script run Test locally entrypoint file, --target-org
script deploy Deploy to Data Cloud --target-org, --name, --package-dir, --package-version, --description

Resources

Notes

  • Code extensions run in isolated Python 3.11 environment
  • Docker is required only for deployment, not for local testing
  • Use SF CLI authentication only (no separate credential files)
  • Scan command auto-detects permissions from code
  • Local run uses actual Data Cloud data (not mocked)
  • Deployments are versioned and can be rolled back in UI
负责Salesforce Data Cloud的摄取与数据湖准备阶段。管理数据流、DLO、转换及Document AI配置,处理非结构化数据摄入。需先验证环境并分类数据集类型(Profile/Engagement/Other),连接设置和身份解析需委托给其他Skill。
创建或管理Data Cloud数据流 配置Data Lake Objects (DLOs) 设置数据转换规则 配置Document AI 询问数据如何摄入到Data Cloud
skills/data360-prepare/SKILL.md
npx skills add forcedotcom/sf-skills --skill data360-prepare -g -y
SKILL.md
Frontmatter
{
    "name": "data360-prepare",
    "metadata": {
        "version": "1.0"
    },
    "description": "Salesforce Data Cloud Prepare phase. Use this skill when the user creates or manages Data Cloud data streams, DLOs, transforms, or Document AI configurations. TRIGGER when: user creates or manages Data Cloud data streams, DLOs, transforms, or Document AI configurations, or asks about ingestion into Data Cloud. DO NOT TRIGGER when: the task is connection setup only (use data360-connect), DMOs and identity resolution (use data360-harmonize), or query\/search work (use data360-query).",
    "compatibility": "Requires an external community sf data360 CLI plugin and a Data Cloud-enabled org"
}

data360-prepare: Data Cloud Prepare Phase

Use this skill when the user needs ingestion and lake preparation work: data streams, Data Lake Objects (DLOs), transforms, Document AI, unstructured ingestion, or the handoff from connector setup into a live stream.

When This Skill Owns the Task

Use data360-prepare when the work involves:

  • sf data360 data-stream *
  • sf data360 dlo *
  • sf data360 transform *
  • sf data360 docai *
  • choosing how data should enter Data Cloud
  • rerunning or rescanning ingestion after a source update
  • preparing Ingestion API-backed streams after connector setup is complete

Delegate elsewhere when the user is:


Required Context to Gather First

Ask for or infer:

  • target org alias
  • source connection name
  • source object / dataset / document source
  • desired stream type
  • DLO naming expectations
  • whether the user is creating, updating, running, or deleting a stream
  • whether the source is CRM, a database connector, an unstructured file source, or an Ingestion API feed

Core Operating Rules

  • Verify the external plugin runtime before running Data Cloud commands.
  • Run the shared readiness classifier before mutating ingestion assets: node ../data360-orchestrate/scripts/diagnose-org.mjs -o <org> --phase prepare --json.
  • Prefer inspecting existing streams and DLOs before creating new ingestion assets.
  • Suppress linked-plugin warning noise with 2>/dev/null for normal usage.
  • Treat DLO naming and field naming as Data Cloud-specific, not CRM-native.
  • Confirm whether each dataset should be treated as Profile, Engagement, or Other before creating the stream.
  • Distinguish stream-level refresh from connection-level reruns when working with unstructured sources.
  • Use UI setup intentionally when initial stream or unstructured asset creation is platform-gated.
  • Hand off to Harmonize only after ingestion assets are clearly healthy.

Recommended Workflow

1. Classify readiness for prepare work

node ../data360-orchestrate/scripts/diagnose-org.mjs -o <org> --phase prepare --json

2. Inspect existing ingestion assets

sf data360 data-stream list -o <org> 2>/dev/null
sf data360 dlo list -o <org> 2>/dev/null

3. Confirm the stream category before creation

Use these rules when suggesting categories:

Category Use for Typical requirement
Profile person/entity records primary key
Engagement time-based events or interactions primary key + event time field
Other reference/configuration/supporting datasets primary key

When the source is ambiguous, ask the user explicitly whether the dataset should be treated as Profile, Engagement, or Other.

4. Create or inspect streams intentionally

sf data360 data-stream get -o <org> --name <stream> 2>/dev/null
sf data360 data-stream create-from-object -o <org> --object Contact --connection SalesforceDotCom_Home 2>/dev/null
sf data360 data-stream create -o <org> -f stream.json 2>/dev/null
sf data360 data-stream run -o <org> --name <stream> 2>/dev/null

5. Check DLO shape

sf data360 dlo get -o <org> --name Contact_Home__dll 2>/dev/null

6. Choose the right refresh mechanism

Use the smaller refresh scope that matches the user goal:

sf data360 data-stream run -o <org> --name <stream> 2>/dev/null
sf data360 connection run-existing -o <org> --name <connection-id> 2>/dev/null
  • data-stream run is the closest match to a stream-level refresh or re-scan.
  • connection run-existing runs at the connection level and can be useful for some connector workflows, but it is not a reliable replacement for stream refresh on unstructured sources.
  • For unstructured document connectors, prefer data-stream run when the goal is to re-scan newly added or changed files.

7. Handle unstructured sources deliberately

For SharePoint-style document ingestion, a minimal unstructured DLO payload can look like:

{
  "name": "my_udlo",
  "label": "My UDLO",
  "category": "Directory_Table",
  "dataSource": {
    "sourceType": "SF_DRIVE",
    "directoryAndFilesDetails": [
      {
        "dirName": "SPUnstructuredDocument/<CONNECTION_ID>/<SITE_ID>",
        "fileName": "*"
      }
    ],
    "sourceConfig": {
      "reservedPrefix": "$dcf_content$"
    }
  }
}

Use the UI for the first-time unstructured setup when the user needs the richer end-to-end pipeline. The UI path can seed additional document metadata fields and downstream assets that a bare CLI DLO create flow may not provision automatically.

8. Use the local Ingestion API example for send-data workflows

For external systems pushing records into Data Cloud:

  1. create the connector in data360-connect
  2. upload the schema with sf data360 connection schema-upsert
  3. create the stream in the UI when required
  4. send records with the local example in examples/ingestion-api/
cd examples/ingestion-api
cp .env.example .env
python3 send-data.py

Key details:

  • auth is a staged flow: JWT → Salesforce token → Data Cloud token
  • the ingestion endpoint uses the tenant URL, not the Salesforce instance URL
  • 202 means the payload was accepted for processing, not that records are queryable immediately
  • validation failures often surface in the Problem Records DLO family

9. Only then move into harmonization

Once the stream and DLO are healthy, hand off to data360-harmonize.


High-Signal Gotchas

  • CRM-backed stream behavior is not the same as fully custom connector-framework ingestion.
  • sf data360 data-stream run and sf data360 connection run-existing are not interchangeable; prefer stream-level refresh for unstructured rescans.
  • SFDC streams sync on a platform-managed schedule; data-stream run is not the general control path for CRM connector refresh.
  • Some external database connectors can be created via API while stream creation still requires UI flow or org-specific browser automation. Do not promise a pure CLI stream-creation path for every connector type.
  • Initial SharePoint-style unstructured setup can be richer in the UI than in a minimal CLI DLO create flow.
  • Stream deletion can also delete the associated DLO unless the delete mode says otherwise.
  • DLO field naming differs from CRM field naming, including __c_c transformations.
  • Query DLO record counts with Data Cloud SQL instead of assuming list output is sufficient.
  • CdpDataStreams means the stream module is gated for the current org/user; guide the user to provisioning/permissions review instead of retrying blindly.

Output Format

Prepare task: <stream / dlo / transform / docai>
Source: <connection + object>
Target org: <alias>
Artifacts: <stream names / dlo names / json definitions>
Verification: <passed / partial / blocked>
Next step: <harmonize or retrieve>

References

通过 Salesforce CLI 和 REST API 获取 Data Cloud 中 DLO 和 DMO 的 Schema 信息,包括字段定义、数据类型及元数据。支持列出所有对象或查询特定对象的详细结构,帮助用户探索和理解数据模型。
用户需要查看 Data Cloud 中的所有 DLO 或 DMO 列表 用户需要获取特定 DLO 或 DMO 的详细字段 Schema 和元数据 用户正在探索 Data Cloud 的数据结构
skills/data360-schema-get/SKILL.md
npx skills add forcedotcom/sf-skills --skill data360-schema-get -g -y
SKILL.md
Frontmatter
{
    "name": "data360-schema-get",
    "metadata": {
        "version": "1.0"
    },
    "description": "Retrieve Data Lake Object (DLO) and Data Model Object (DMO) schema information from Salesforce Data Cloud using REST APIs. Use this skill when you need to inspect DLO or DMO field definitions, data types, or metadata. Takes org alias and optional DLO\/DMO name as parameters."
}

data360-schema-get Skill

Overview

This skill retrieves Data Lake Object (DLO) and Data Model Object (DMO) schema information from Salesforce Data Cloud using the SSOT REST API. It can list all DLOs or DMOs in an org, or retrieve detailed schema for a specific DLO or DMO.

When to Use

  • User wants to see all DLOs or DMOs in a Data Cloud org
  • User needs field schema for a specific DLO or DMO
  • User is exploring Data Cloud data structures
  • User needs to understand DLO or DMO field types and metadata

Prerequisites

  • SF CLI installed and authenticated to target org
  • Org has Data Cloud enabled
  • User has appropriate Data Cloud permissions

Skill Execution

Parameters

  1. org_alias (required): The SF CLI org alias (e.g., 'afvibe', 'myorg')
  2. dlo_name (optional): Specific DLO developer name (e.g., 'Employee__dll')
  3. dmo_name (optional): Specific DMO developer name (e.g., 'Individual__dlm')

Step 1: Discover Connected Org

First, run sf org list to find out which org is connected and extract the alias to use for all subsequent calls:

sf org list

Example output:

┌────┬───────┬──────────────────────────┬────────────────────┬───────────┐
│    │ Alias │ Username                 │ Org Id             │ Status    │
├────┼───────┼──────────────────────────┼────────────────────┼───────────┤
│ 🍁 │ myorg │ chandresh@afvidedemo.org │ 00DKZ00000b80NT2AY │ Connected │
└────┴───────┴──────────────────────────┴────────────────────┴───────────┘

Extract the Alias value (e.g., myorg) from the output and use it as the <org_alias> for all subsequent calls. Use --all to see expired and deleted scratch orgs as well.

Step 2: Validate SF CLI Authentication

Before making API calls, verify the org is connected:

sf org display --target-org <org_alias> --json

If not connected, inform user to run:

sf org login web --alias <org_alias>

Step 3a: Execute DLO Schema Script

The Python scripts are bundled with this skill in the scripts/ subdirectory.

To list all DLOs:

python3 ./scripts/get_dlo_schema.py <org_alias>

To get specific DLO schema:

python3 ./scripts/get_dlo_schema.py <org_alias> <dlo_name>

Step 3b: Execute DMO Schema Script

To list all DMOs:

python3 ./scripts/get_dmo_schema.py <org_alias>

To get specific DMO schema:

python3 ./scripts/get_dmo_schema.py <org_alias> <dmo_name>

Step 4: Present Results

Parse and present the results in a user-friendly format:

For DLO List:

  • Show DLO name, label, category, and ID
  • Indicate total count
  • Highlight DLOs with data (totalRecords > 0)

For DLO Schema:

  • Show basic info (name, label, category, status)
  • List all fields with:
    • Field name
    • Data type
    • Primary key indicator
    • Nullable status
  • Highlight custom fields (exclude system fields like DataSource__c, cdp_sys_*)
  • Show record count if available

For DMO List:

  • Show DMO name, label, category, and ID
  • Indicate total count

For DMO Schema:

  • Show basic info (name, label, category, description)
  • List all fields with:
    • Field name
    • Data type
    • Primary key indicator
    • Nullable status
  • Show dataspace information if available

Step 5: Offer Next Steps

After displaying results, suggest relevant follow-up actions:

  • Query data from the DLO
  • Create calculated insights
  • Build segments
  • Set up data streams
  • Create DMO mappings

API Endpoints Used

List All DLOs

GET /services/data/v64.0/ssot/data-lake-objects

Response structure:

{
  "dataLakeObjects": [
    {
      "name": "Employee__dll",
      "label": "Employee",
      "category": "Profile",
      "id": "1dlXXXXXXXXXXXXXXX",
      "status": "ACTIVE",
      "totalRecords": 12,
      "fields": [...]
    }
  ],
  "totalSize": 5
}

Get DLO Schema

GET /services/data/v64.0/ssot/data-lake-objects/{dlo_name}

Response structure (same as individual object in list response, but wrapped in paginated format).

List All DMOs

GET /services/data/v64.0/ssot/data-model-objects

Response structure:

{
  "dataModelObjects": [
    {
      "name": "Individual__dlm",
      "label": "Individual",
      "category": "Profile",
      "id": "0dmXXXXXXXXXXXXXXX",
      "fields": [...]
    }
  ],
  "totalSize": 10
}

Get DMO Schema

GET /services/data/v64.0/ssot/data-model-objects/{dmo_name}

Response structure (same as individual object in list response, but wrapped in paginated format).

Error Handling

Common Issues:

  1. Org not connected

    • Message: "Org not connected"
    • Solution: Ask user to authenticate via SF CLI
  2. DLO not found

    • Message: "DLO 'XYZ__dll' not found"
    • Solution: List all DLOs first to verify name
  3. DMO not found

    • Message: "DMO 'XYZ__dlm' not found"
    • Solution: List all DMOs first to verify name
  4. Permission issues

    • Message: HTTP 403 errors
    • Solution: Verify user has Data Cloud permissions
  5. API version mismatch

    • Current: v64.0
    • Solution: Script can be updated for newer API versions

Example Usage

Example 1: List all DLOs

User: "Show me all DLOs in afvibe org"

Response:
1. Run sf org list to discover connected org alias
2. Authenticate to afvibe
3. Run: python3 ./scripts/get_dlo_schema.py afvibe
4. Display formatted list of DLOs

Example 2: Get specific DLO schema

User: "Get the schema for Employee__dll in afvibe"

Response:
1. Run sf org list to discover connected org alias
2. Authenticate to afvibe
3. Run: python3 ./scripts/get_dlo_schema.py afvibe Employee__dll
4. Display field schema with types and metadata

Example 3: Explore DLOs then get schema

User: "What DLOs exist in myorg and show me the schema for the Employee one"

Response:
1. Run sf org list to discover connected org alias
2. List all DLOs in myorg
3. Identify Employee__dll
4. Get detailed schema for Employee__dll
5. Present both results

Example 4: List all DMOs

User: "Show me all DMOs in afvibe org"

Response:
1. Run sf org list to discover connected org alias
2. Authenticate to afvibe
3. Run: python3 ./scripts/get_dmo_schema.py afvibe
4. Display formatted list of DMOs

Example 5: Get specific DMO schema

User: "Get the schema for Individual__dlm in afvibe"

Response:
1. Run sf org list to discover connected org alias
2. Authenticate to afvibe
3. Run: python3 ./scripts/get_dmo_schema.py afvibe Individual__dlm
4. Display field schema with types and metadata

Example 6: Explore DMOs then get schema

User: "What DMOs exist in myorg and show me the schema for the Individual one"

Response:
1. Run sf org list to discover connected org alias
2. List all DMOs in myorg
3. Identify Individual__dlm
4. Get detailed schema for Individual__dlm
5. Present both results

Output Format

DLO List Output

Found 5 DLOs in org 'afvibe':

1. DataCustomCodeLogs__dll
   Label: DataCustomCodeLogs
   Category: Engagement
   Records: 233

2. Employee__dll
   Label: Employee
   Category: Profile
   Records: 12

[...]

DLO Schema Output

DLO: Employee__dll
Label: Employee
Category: Profile
Status: ACTIVE
Records: 12

Custom Fields:
  • id__c (Text) - Primary Key
  • name__c (Text)
  • position__c (Text)
  • manager_id__c (Number)

System Fields:
  • DataSource__c (Text)
  • InternalOrganization__c (Text)
  • cdp_sys_SourceVersion__c (Text)

Next steps:
- Query data: SELECT * FROM Employee__dll LIMIT 10
- Create segment based on position field
- Set up data stream for real-time updates

DMO List Output

Found 10 DMOs in org 'afvibe':

1. Individual__dlm
   Label: Individual
   Category: Profile

2. ContactPointEmail__dlm
   Label: Contact Point Email
   Category: Profile

[...]

DMO Schema Output

DMO: Individual__dlm
Label: Individual
Category: Profile
Description: Represents an individual person

Fields:
  • Id__c (Text) - Primary Key
  • FirstName__c (Text)
  • LastName__c (Text)
  • BirthDate__c (DateTime)

Next steps:
- Query data: SELECT * FROM Individual__dlm LIMIT 10
- View DLO mappings to this DMO
- Create calculated insights

Notes

  • DLO names always end with __dll suffix
  • DMO names always end with __dlm suffix
  • Field names always end with __c suffix
  • System fields (DataSource__c, KQ_, cdp_sys_) are automatically added
  • Primary key fields are required for DLO and DMO queries
  • API supports pagination (limit/offset) for large result sets

Related Skills

  • datakit_workflow: For DMO mapping operations
  • datakit_validation: For validating datakit configurations
  • Use this skill before creating DMO mappings to understand source DLO structure
指导Agent应用SLDS v2构建UI。涵盖组件选型层级(LBC优先)、蓝图使用、样式钩子及工具类,强调验证存在性并排除无障碍与LWC机制细节。
build a modal create a form data table SLDS styling style with hooks add an icon
skills/design-systems-slds-apply/SKILL.md
npx skills add forcedotcom/sf-skills --skill design-systems-slds-apply -g -y
SKILL.md
Frontmatter
{
    "name": "design-systems-slds-apply",
    "metadata": {
        "version": "1.0"
    },
    "description": "Apply SLDS-compliant UI using the correct blueprints, styling hooks, utility classes, and icons. Use when building any UI that needs SLDS, choosing between Lightning Base Components and SLDS Blueprints, applying styling hooks for theming, using utility classes for layout and spacing, or selecting icons. Triggers include \"build a modal\", \"create a form\", \"data table\", \"SLDS styling\", \"style with hooks\", \"add an icon\"."
}

Applying SLDS

The Salesforce Lightning Design System (SLDS) is a CSS framework with thousands of artifacts. This skill teaches agents how to find and correctly use them.

Version: This skill targets SLDS v2. Legacy --lwc-* tokens and slds-*--modifier syntax are deprecated.

Audit scope: The companion design-systems-slds-validate skill analyzer only scans .css, .html, and .js files. Use it directly for LWC and similar HTML/CSS/JS components; treat it as a partial signal for JSX/TSX or other framework-specific template formats and supplement with manual review.

What is SLDS?

Artifact Count Description
Lightning Base Components ~70 Pre-built LWC components (LWC only)
SLDS Blueprints 85 CSS/HTML patterns for any framework
Styling Hooks 523 CSS custom properties (--slds-g-*) for theming
Utility Classes 1,147 Rapid styling classes for spacing, layout, visibility
Icons 1,732 SVG icons across 5 categories

Scope

This skill covers:

  • Which blueprint to use for a given UI pattern
  • How to style with hooks (color, spacing, typography, shadows, borders)
  • Which utility classes to use for layout, spacing, visibility
  • Which icon to use and from which category
  • SLDS naming conventions, class structure, hook syntax

This skill includes basic accessibility reminders (icon alt text, focus outlines, color-not-sole-indicator) in the validation checklists. Full WCAG compliance requires a dedicated accessibility review.

This skill does NOT cover (use companion skills):

  • Design decisions -- visual hierarchy, composition, interaction patterns
  • LWC mechanics -- component structure, @wire, @api, lifecycle, events (not yet available)
  • Full accessibility -- WCAG conformance, ARIA patterns, keyboard navigation, focus management, contrast ratios (not yet available)

Component Selection Hierarchy

Always follow this order:

1. Lightning Base Components (LWC only)    ← Check first
2. SLDS Blueprints (any framework)         ← Use exact SLDS classes
3. Custom with Styling Hooks               ← Use var(--slds-g-*)
4. Custom CSS (last resort)                ← Still use hooks for values

If building in LWC, check for an LBC first: Lightning Component Library

If no LBC exists (or not using LWC), select an SLDS Blueprint. See references/component-selection.md.


Core Rules

Do

  • Follow the selection hierarchy: LBC > Blueprint > Hooks > Custom CSS
  • Use var(--slds-g-*, fallback) for all themeable values
  • Create custom classes (my-*, c-*) instead of overriding .slds-*
  • Verify every hook, class, and utility exists before using it — run the search scripts; never assume an artifact exists based on naming patterns (see Verify Before You Use)
  • Pair surface colors with on-surface colors for text
  • Provide alternative-text on every <lightning-icon>

Don't

  • Hard-code colors, spacing, or typography values
  • Override .slds-* classes directly
  • Use deprecated --lwc-* tokens as primary values
  • Use --slds-s-* (shared) hooks -- they are private/internal
  • Reassign hook values -- only reference them with var()
  • Use color alone to convey meaning
  • Invent hook names by interpolating patterns from other families (see Naming Traps below)

Hook Naming Traps

SLDS hook families do NOT all follow the same naming pattern. Agents frequently invent hooks that don't exist by assuming {prefix}-{number} works universally. Always verify a hook exists via the bundled search-hooks.cjs script or assets/hooks-index.json before using it.

Trap 1: Font size hooks are NOT numbered

Wrong (does not exist) Correct Notes
--slds-g-font-size-3 --slds-g-font-scale-1 Font sizes use font-scale-*, not font-size-*
--slds-g-font-size-4 --slds-g-font-scale-2 Only --slds-g-font-size-base exists (base size)
--slds-g-font-size-8 --slds-g-font-scale-6 Scale goes: neg-4 through 10

Rule: For font sizes, use --slds-g-font-size-base (the one base size) or --slds-g-font-scale-* (the numbered scale). Never --slds-g-font-size-N.

Trap 2: Color hooks always require a number

Wrong (does not exist) Correct Notes
--slds-g-color-on-surface --slds-g-color-on-surface-2 All color hooks need a number
--slds-g-color-on-accent --slds-g-color-on-accent-1 Pick 1/2/3 by emphasis level
--slds-g-color-surface --slds-g-color-surface-1 No unnumbered base form

Rule: Every --slds-g-color-* hook ends in a number. Pick by emphasis: -1 (low), -2 (medium), -3 (high).

Trap 3: Not all values have hook equivalents

Some CSS values (e.g., min-width: 7rem for label alignment) have no SLDS hook. This is acceptable:

.c-field-label {
  /* No SLDS hook exists for this width; intentional custom value */
  min-width: 7rem;
}

Rule: When no hook exists, use the value directly with a comment explaining it's intentional. Prefer SLDS grid utilities (slds-size_*) as alternatives to hardcoded widths where possible.


Verify Before You Use

Rule: Never include an SLDS hook, utility class, blueprint class, or icon in generated code without first confirming it exists in the metadata. Guessing based on naming patterns is the primary source of invented artifacts.

Run the appropriate search command before emitting any SLDS artifact:

Artifact Verification command Source of truth
Styling hook (--slds-g-*) node scripts/search-hooks.cjs --prefix "<hook-name>" assets/hooks-index.json
Utility class (slds-*) node scripts/search-utilities.cjs --search "<class-name>" assets/utilities-index.json
Blueprint / CSS class node scripts/search-blueprints.cjs --search "<pattern>" then read the YAML assets/blueprints/components/*.yaml
Icon node scripts/search-icons.cjs --query "<description>" assets/icon-metadata.json

If the search returns no match: do not use the artifact. Find an alternative from the search results or build custom with verified hooks.


Naming Conventions

Use a consistent prefix for custom classes to avoid collision with SLDS:

Pattern Use Case Example
my-* General custom styling my-card-header
c-* LWC component-specific c-accountList-row
[namespace]-* Package/app namespace acme-dashboard-widget

Avoid: generic names (container, wrapper), SLDS-like names (custom-slds-button), BEM on SLDS classes (slds-card__custom-header).

Custom hook namespacing:

:root {
  --my-app-primary: var(--slds-g-color-accent-1);
  --my-app-card-padding: var(--slds-g-spacing-4);
}

Knowledge Map

This skill bundles comprehensive SLDS knowledge. Read files as needed -- don't read everything upfront.

Decision Guides (start here for each task)

File Read when
references/component-selection.md Choosing a component or blueprint
references/styling-decision-guide.md Applying colors, spacing, typography, shadows
references/icons-decision-guide.md Selecting or implementing an icon
references/utilities-quick-ref.md Using utility classes for layout/spacing

Search Scripts (find specific artifacts)

Script What it searches Example
scripts/search-blueprints.cjs 85 blueprint YAMLs --search "dialog"
scripts/search-hooks.cjs 523 styling hooks --prefix "--slds-g-color-accent-"
scripts/search-icons.cjs 1,732 icons with synonyms --query "save button"
scripts/search-utilities.cjs 1,147 utility classes --category "grid"

Deep-Dive Guidance (read for detailed rules)

Folder Content Index
references/overviews/ Foundational concepts (color, spacing, typography, etc.) references/README.md
references/styling-hooks/ Hook categories with detailed usage references/README.md
references/utilities/ 27 utility class categories references/README.md
references/slds-development-guide.md Full SLDS development guide --

Raw Metadata (structured data for lookup)

Do not read metadata JSON files directly — they are too large for agent context (hooks-index.json is 6,000+ lines; icon-metadata.json is 38,000+ lines). Use the search scripts above to query them.

File Content Lines
assets/blueprints/components/*.yaml 85 blueprint specs (classes, variants, a11y, HTML) ~50-200 each
assets/hooks-index.json 523 hooks with values and CSS properties ~6,300
assets/icon-metadata.json 1,732 icons with synonyms for search ~38,500
assets/utilities-index.json 1,147 utility classes with CSS rules ~6,900

Authoring Workflow

Phase 1: Understand the Need

Identify:

  • What UI pattern is needed? (form, table, modal, card, etc.)
  • What framework? (LWC, React, Vue, Angular, vanilla)
  • What data will it display?
  • What states does it need? (loading, empty, error, success)

Phase 2: Select the Artifact

  1. If LWC: Check the Lightning Component Library for an LBC
  2. Search blueprints: node scripts/search-blueprints.cjs --search "<pattern>"
  3. Read the blueprint YAML: assets/blueprints/components/<name>.yaml for exact classes, modifiers, states, and accessibility requirements
  4. No match? Build custom with hooks (see Phase 3)

Details: references/component-selection.md

Phase 3: Apply Styling

  1. Read: references/styling-decision-guide.md
  2. Colors: Classify role (surface, accent, feedback, border) then pick hook
  3. Spacing: Use utility classes (slds-p-*, slds-m-*) or hooks (--slds-g-spacing-*)
  4. Layout: Use grid utilities (slds-grid, slds-col, slds-size_*)
  5. Custom CSS: Use var(--slds-g-*, fallback), custom class prefixes only

Phase 4: Add Icons

  1. Read: references/icons-decision-guide.md
  2. Search: node scripts/search-icons.cjs --query "<description>"
  3. In LWC: Use <lightning-icon> with alternative-text
  4. In non-LWC: Use SVG with slds-icon classes and slds-assistive-text

Phase 5: Validate (Mandatory — Do Not Skip)

Step 1: Run the SLDS linter. This is required. Zero violations is the target.

npx @salesforce-ux/slds-linter@latest lint <component-path>

The linter catches hardcoded values, class overrides, and deprecated tokens. Fix all violations before proceeding. Do not rationalize violations as acceptable.

Step 2: Verify no invented hooks. Confirm every --slds-g-* hook in the output exists in assets/hooks-index.json. Cross-reference against the T051 check in checklists.md.

Step 3: Run through checklists.md for the checks the linter cannot automate:

  • All var(--slds-g-*) have fallback values (T002)
  • Surface/accent/feedback color hooks are properly paired (T010–T013)
  • Spacing uses hooks or utility classes — no magic px values (T020–T021)
  • Font sizes use --slds-g-font-scale-*, not --slds-g-font-size-N (T031)
  • All icons have accessibility text (A004)
  • Custom classes use my-* or c-* prefix (Q010)

Step 4 (optional): Run the full quality audit using the design-systems-slds-validate skill for a scored report before code review or deployment. Use it directly for LWC / HTML-CSS-JS components; for JSX/TSX outputs, treat the result as partial coverage only. Target a B grade (≥80) or higher before marking work complete.


Quick Reference

Common Hook Patterns

/* Surface + text pairing (always use numbered variants) */
background: var(--slds-g-color-surface-1, #ffffff);
color: var(--slds-g-color-on-surface-2, #181818);

/* Standard padding */
padding: var(--slds-g-spacing-4, 1rem);

/* Card-like container */
border-radius: var(--slds-g-radius-border-2, 0.25rem);
box-shadow: var(--slds-g-shadow-1, 0 2px 4px rgba(0,0,0,0.1));

/* Accent for primary actions */
background: var(--slds-g-color-accent-1, #0176d3);
color: var(--slds-g-color-on-accent-1, #ffffff);

/* Typography -- use font-scale-*, NOT font-size-* (only font-size-base exists) */
font-size: var(--slds-g-font-scale-2, 0.875rem);

Common Utility Patterns

<!-- Responsive grid -->
<div class="slds-grid slds-wrap slds-gutters">
  <div class="slds-col slds-size_1-of-1 slds-medium-size_1-of-2">...</div>
</div>

<!-- Spacing -->
<div class="slds-p-around_medium slds-m-bottom_small">...</div>

<!-- Truncation -->
<p class="slds-truncate" title="Full text here">Full text here</p>

Examples

See examples.md for worked examples demonstrating the full workflow from intent to SLDS artifact selection.

Validation

See checklists.md for validation checklists aligned with the design-systems-slds-validate skill.

Resources

Resource URL
SLDS Website https://www.lightningdesignsystem.com/
Lightning Component Library https://developer.salesforce.com/docs/component-library/overview/components
SLDS Linter https://developer.salesforce.com/docs/platform/slds-linter/guide
Styling Hooks Reference https://www.lightningdesignsystem.com/2e1ef8501/p/591960-global-styling-hooks
通过 sf org assign permset 命令将权限集分配给组织用户,支持默认管理员、指定用户及多权限集批量操作。仅使用 Bash 工具执行,严禁用于查询或创建权限集。
用户请求分配或授予权限集给用户 用户请求为特定用户或管理员添加权限
skills/dx-org-permission-set-assign/SKILL.md
npx skills add forcedotcom/sf-skills --skill dx-org-permission-set-assign -g -y
SKILL.md
Frontmatter
{
    "name": "dx-org-permission-set-assign",
    "metadata": {
        "version": "1.0"
    },
    "description": "ALWAYS USE THIS SKILL to assign permission sets to org users. Assign one or more permission sets to org users using the sf org assign permset command. TRIGGER when the user asks to assign, grant, give, add, or apply permission sets to users, admins, specific orgs, or specific users. Supports granting permissions, giving access, and adding permission sets to default admin or specific users via --on-behalf-of. DO NOT TRIGGER for listing permission sets or checking user permissions.",
    "compatibility": "Salesforce CLI (sf) v2+"
}

dx-org-permission-set-assign

Assigns one or more permission sets to org users using sf org assign permset. Handles all variants: default admin user, specific org targets, multiple permission sets, and assignment to specific users.


Tool Restrictions

Use ONLY the Bash tool to execute sf org assign permset. Do NOT use MCP tools like assign_permission_set — ignore them completely.


Scope

  • In scope: Assigning permission sets to users via sf org assign permset
  • Out of scope: Creating permission sets (use platform-permission-set-generate), listing permission sets, checking user permissions

Required Inputs

Infer from the user's request:

  • Permission set name(s): Extract from user message (can be multiple)
  • Target org: Use default unless specific alias/username mentioned
  • Target user(s): Default is org's default admin user; use --on-behalf-of if specific users mentioned

Workflow

  1. Match user request to command in table below
  2. Execute via Bash tool: sf org assign permset with appropriate flags and --json flag
  3. Return result

If error occurs, check the failures array in JSON output for details.

Command Decision Table

User intent Execute via Bash tool
Assign one permission set to default admin sf org assign permset --name <PermSetName> --json
Assign multiple permission sets to default admin sf org assign permset --name <PermSet1> --name <PermSet2> --json
Assign to specific org sf org assign permset --name <PermSetName> --target-org <alias> --json
Assign to specific user(s) sf org assign permset --name <PermSetName> --on-behalf-of <username1> --on-behalf-of <username2> --json
Assign multiple sets to specific users sf org assign permset --name <PermSet1> --name <PermSet2> --on-behalf-of <username1> --on-behalf-of <username2> --json

Rules / Constraints

Constraint Rationale
Always use --json flag Provides structured output for reliable parsing and error handling
Permission set names are case-sensitive Use exact API names as they appear in the org
Multiple --name flags can be combined in one command More efficient than separate commands per permission set
Multiple --on-behalf-of flags assign to multiple users Batch assignment in single command; processed sequentially to avoid auth file collisions
Use CLI username aliases, not Salesforce User.Alias field The --target-org and --on-behalf-of flags expect CLI aliases set via sf alias set, not the User object's Alias field
Duplicate assignments are idempotent Re-assigning an already-assigned permission set succeeds silently
Partial success is possible Command can return both successes and failures in one run; non-zero exit code if any failures

Gotchas

Issue Resolution
Permission set name with spaces Enclose in double quotes: --name "Permission Set Name"
"PermissionSet not found" error Verify permission set exists in target org; check for typos in name
Assignment succeeds but user doesn't see permissions Check <hasActivationRequired> in permission set metadata — may need manual activation in Setup
"User not found" error Username/alias doesn't exist in target org — verify with sf org display user --target-org <alias>
Partial success (some users succeed, others fail) Check JSON output — command returns both successes and failures arrays; exit code will be non-zero if any failures occurred

Output Expectations

The command returns JSON output with status code and result details.

See examples/success_output.json and examples/error_output.json for response structures.


Reference File Index

File When to read
examples/success_output.json To understand successful assignment response structure
examples/error_output.json To handle common error scenarios
references/cli_flags.md For detailed explanation of all available flags
通过 Salesforce CLI 切换默认目标 Org。支持指定别名或用户名,自动列出已认证 Org 供选择。区分本地(默认)和全局配置范围,并提供设置后的验证步骤及故障排查建议。
用户要求切换 Salesforce Org 用户希望更改 CLI 命令的目标环境 用户提及使用特定别名、沙盒或生产环境
skills/dx-org-switch/SKILL.md
npx skills add forcedotcom/sf-skills --skill dx-org-switch -g -y
SKILL.md
Frontmatter
{
    "name": "dx-org-switch",
    "metadata": {
        "version": "1.0"
    },
    "description": "Switches the active Salesforce org (default target-org) using the Salesforce CLI. Use whenever someone wants to change which org CLI commands run against — whether they say \"switch org\", \"change default org\", \"set my org to\", \"use alias\", \"point to\", or describe wanting to work against a specific org, scratch org, sandbox, or production.",
    "compatibility": "Salesforce CLI (sf) v2+"
}

Steps

  1. Identify the org: the user provides a username or alias (orgIdentifier). If not provided, run sf org list to show authenticated orgs and ask the user which one to use.
  2. Set the default org:
    • Local (default): sf config set target-org <orgIdentifier>
      • Applies only within the current project directory. Use this for normal project work.
    • Global (only if user explicitly requests): sf config set target-org <orgIdentifier> --global
      • Applies system-wide across all directories. Use when working outside a project or when the user asks for global scope.
    • If this fails, report the error and suggest running sf org login web if the org may not be authorized.
  3. Verify:
    • sf config get target-org --json
    • Note: the JSON output does not include a scope/location field — it cannot confirm whether the value is local or global. Confirm the value only, e.g.: target-org is now set to: <value>
    • If it fails, report the error and advise running sf config get target-org.

Notes

用于 Salesforce LWC 开发,涵盖组件创建、编辑、Jest 测试及 Wire 服务。遵循 PICKLES 方法论,支持 LDS/Apex/GraphQL 集成与 SLDS 样式。处理 lwc 相关文件时触发,排除 Apex 或 Aura 任务。
用户创建或编辑 LWC 组件 修改 lwc 目录下的 js/html/css/xml 文件 询问关于 wire 服务、SLDS 或 Jest LWC 测试的问题
skills/experience-lwc-generate/SKILL.md
npx skills add forcedotcom/sf-skills --skill experience-lwc-generate -g -y
SKILL.md
Frontmatter
{
    "name": "experience-lwc-generate",
    "metadata": {
        "version": "1.1"
    },
    "description": "Lightning Web Components with PICKLES methodology and 165-point scoring. Use this skill when the user creates or edits LWC components, builds wire service patterns, or writes Jest tests for LWC. TRIGGER when: user creates\/edits LWC components, touches lwc\/**\/*.js, .html, .css, .js-meta.xml files, or asks about wire service, SLDS, or Jest LWC tests. DO NOT TRIGGER when: Apex classes (use platform-apex-generate), Aura components, or Visualforce."
}

experience-lwc-generate: Lightning Web Components Development

Use this skill when the user needs Lightning Web Components: LWC bundles, wire patterns, Apex/GraphQL integration, SLDS 2 styling, accessibility, performance work, or Jest unit tests.

When This Skill Owns the Task

Use experience-lwc-generate when the work involves:

  • lwc/**/*.js, .html, .css, .js-meta.xml
  • component scaffolding and bundle design
  • wire service, Apex integration, GraphQL integration
  • SLDS 2, dark mode, and accessibility work
  • Jest unit tests for LWC

Delegate elsewhere when the user is:


Required Context to Gather First

Ask for or infer:

  • component purpose and target surface
  • data source: LDS, Apex, GraphQL, LMS, or external system via Apex
  • whether the user needs tests
  • whether the component must run in Flow, App Builder, Experience Cloud, or dashboard contexts
  • accessibility and styling expectations

Recommended Workflow

1. Choose the right architecture

Use the PICKLES mindset:

  • prototype
  • integrate the right data source
  • compose component boundaries
  • define interaction model
  • use platform libraries
  • optimize execution
  • enforce security

2. Choose the right data access pattern

Need Default pattern
single-record UI LDS / getRecord
simple CRUD form base record form components
complex server query Apex @AuraEnabled(cacheable=true)
related graph data GraphQL wire adapter
cross-DOM communication Lightning Message Service

3. Start from an asset when useful

Use provided assets for:

  • basic component bundles
  • datatables
  • modal patterns
  • Flow screen components
  • GraphQL components
  • LMS message channels
  • Jest tests
  • TypeScript-enabled components

4. Validate for frontend quality

Check:

  • accessibility
  • SLDS 2 / dark mode compliance
  • event contracts
  • performance / rerender safety
  • Jest coverage when required

5. Hand off supporting backend or deploy work

Use:


High-Signal Rules

  • prefer platform base components over reinventing controls
  • use @wire for reactive read-only use cases; imperative calls for explicit actions and DML paths
  • do not introduce inaccessible custom UI
  • avoid hardcoded colors; use SLDS 2-compatible styling hooks / variables
  • avoid rerender loops in renderedCallback()
  • keep component communication patterns explicit and minimal

Output Format

When finishing, report in this order:

  1. Component(s) created or updated
  2. Data access pattern chosen
  3. Files changed
  4. Accessibility / styling / testing notes
  5. Next implementation or deploy step

Suggested shape:

LWC work: <summary>
Pattern: <wire / apex / graphql / lms / flow-screen>
Files: <paths>
Quality: <a11y, SLDS2, dark mode, Jest>
Next step: <deploy, add controller, or run tests>

Local Development Server

Preview LWC components locally with hot reload — no deployment needed. Run the commands in scripts/local-dev-preview.sh to start a local dev session for a component, app, or Experience Cloud site.

Local Dev commands install just-in-time on first run. They are long-running processes that open a browser with live preview. Changes to .js, .html, and .css files auto-reload instantly. Requires an active org connection for data and Apex callouts.


Cross-Skill Integration

Need Delegate to Reason
Apex controller or service platform-apex-generate backend logic
embed in Flow screens automation-flow-generate declarative orchestration
deploy component bundle platform-metadata-deploy org rollout
create supporting metadata (message channels, objects) platform-metadata-deploy metadata deployment

Reference File Index

Start here

Accessibility / performance / state

Integration / advanced features

Asset templates

Scripts


Score Guide

Score Meaning
150+ production-ready LWC bundle
125–149 strong component with minor polish left
100–124 functional but review recommended
< 100 needs significant improvement
当项目包含uiBundles目录且需添加认证或搜索功能时激活。通过CLI安装预构建的认证(登录/会话)和搜索组件,避免重复开发。流程包括搜索现有代码、列出/描述/安装特性及处理冲突与示例集成。
用户希望在UI Bundle应用中添加认证功能 用户希望在UI Bundle应用中添加搜索功能 检测到项目中存在 uiBundles/*/src/ 目录
skills/experience-ui-bundle-features-generate/SKILL.md
npx skills add forcedotcom/sf-skills --skill experience-ui-bundle-features-generate -g -y
SKILL.md
Frontmatter
{
    "name": "experience-ui-bundle-features-generate",
    "metadata": {
        "version": "1.0"
    },
    "description": "MUST activate when the project contains a uiBundles\/*\/src\/ directory and the user wants to add authentication or search to their app. Use this skill when adding authentication or search to a UI bundle app. Only covers two features: authentication (login, logout, protected routes, session management) and search (global search across pages and content). Always use this skill for these two features instead of building from scratch."
}

UI Bundle Features

Installing Pre-built Features

Always check for an existing feature before building something from scratch. The features CLI installs pre-built, tested packages into Salesforce UI bundles — from foundational UI libraries (shadcn/ui) to full-stack capabilities (authentication, search, navigation, GraphQL, Agentforce AI).

Workflow

  1. Search project code first — check src/ for existing implementations before installing anything. Scope searches to src/ to avoid matching node_modules/ or dist/.

  2. Search available features — use npx @salesforce/ui-bundle-features list with --search <query> to filter by keyword. Use --verbose for full descriptions.

  3. Describe a feature — use npx @salesforce/ui-bundle-features describe <feature> to see components, dependencies, copy operations, and example files.

  4. Install — use npx @salesforce/ui-bundle-features install <feature> --ui-bundle-dir <name>. Key options:

    • --dry-run to preview changes
    • --yes for non-interactive mode (skips conflicts)
    • --on-conflict error to detect conflicts, then --conflict-resolution <file> to resolve them

If no matching feature is found, ask the user before building a custom implementation — a relevant feature may exist under a different name.

Conflict Handling

In non-interactive environments, use the two-pass approach: first run with --on-conflict error to detect conflicts, then create a resolution JSON file ({ "path": "skip" | "overwrite" }) and re-run with --conflict-resolution.

Post-install: Integrating Example Files

Features may include __example__ files showing integration patterns. For each:

  1. Read the example file to understand the pattern
  2. Read the target file (shown in describe output)
  3. Apply the pattern from the example into the target
  4. Delete the example file after successful integration

Hint Placeholders

Some copy paths use <descriptive-name> placeholders (e.g., <desired-page-with-search-input>) that the CLI does not resolve. After installation, rename or relocate these files to the intended target, or integrate their patterns into an existing file.

为UI Bundle应用提供文件上传API,支持进度跟踪和Salesforce ContentVersion集成。仅导出编程式upload()函数,需自行构建UI组件。包含基础上传、即时关联记录及延迟关联三种模式,严禁使用FormData或XHR从头实现。
项目包含 uiBundles/*/src/ 目录 需要添加文件上传功能 涉及文件的上传、附加或拖放操作
skills/experience-ui-bundle-file-upload-generate/SKILL.md
npx skills add forcedotcom/sf-skills --skill experience-ui-bundle-file-upload-generate -g -y
SKILL.md
Frontmatter
{
    "name": "experience-ui-bundle-file-upload-generate",
    "metadata": {
        "version": "1.0"
    },
    "description": "MUST activate when the project contains a uiBundles\/*\/src\/ directory and the task involves uploading, attaching, or dropping files. Use this skill when adding file upload functionality to a UI bundle app. Provides progress tracking and Salesforce ContentVersion integration. This feature provides programmatic APIs ONLY — build custom UI using the upload() API. ALWAYS use this instead of building file upload from scratch with FormData or XHR."
}

File Upload API (workflow)

When the user wants file upload functionality in a React UI bundle, follow this workflow. This feature provides APIs only — you must build the UI components yourself using the provided APIs.

CRITICAL: This is an API-only package

The package exports programmatic APIs, not React components or hooks. You will:

  • Use the upload() function to handle file uploads with progress tracking
  • Build your own custom UI (file input, dropzone, progress bars, etc.)
  • Track upload progress through the onProgress callback

Do NOT:

  • Expect pre-built components like <FileUpload /> — they are not exported
  • Try to import React hooks like useFileUpload — they are not exported
  • Look for dropzone components — they are not exported

The source code contains reference components for demonstration, but they are not available as imports. Use them as examples to build your own UI.

1. Install the package

npm install @salesforce/ui-bundle-template-feature-react-file-upload

Dependencies are automatically installed:

  • @salesforce/ui-bundle (API client)
  • @salesforce/platform-sdk (data SDK; the old @salesforce/sdk-data name is dead — see the experience-ui-bundle-salesforce-data-access skill)

2. Understand the three upload patterns

Pattern A: Basic upload (no record linking)

Upload files to Salesforce and get back contentBodyId for each file. No ContentVersion record is created.

When to use:

  • User wants to upload files first, then create/link them to a record later
  • Building a multi-step form where the record doesn't exist yet
  • Deferred record linking scenarios
import { upload } from "@salesforce/ui-bundle-template-feature-react-file-upload";

const results = await upload({
  files: [file1, file2],
  onProgress: (progress) => {
    console.log(`${progress.fileName}: ${progress.status} - ${progress.progress}%`);
  },
});

// results[0].contentBodyId: "069..." (always available)
// results[0].contentVersionId: undefined (no record linked)

Pattern B: Upload with immediate record linking

Upload files and immediately link them to an existing Salesforce record by creating ContentVersion records.

When to use:

  • Record already exists (Account, Opportunity, Case, etc.)
  • User wants files immediately attached to the record
  • Direct upload-and-attach scenarios
import { upload } from "@salesforce/ui-bundle-template-feature-react-file-upload";

const results = await upload({
  files: [file1, file2],
  recordId: "001xx000000yyyy", // Existing record ID
  onProgress: (progress) => {
    console.log(`${progress.fileName}: ${progress.status} - ${progress.progress}%`);
  },
});

// results[0].contentBodyId: "069..." (always available)
// results[0].contentVersionId: "068..." (linked to record)

Pattern C: Deferred record linking (record creation flow)

Upload files without a record, then link them after the record is created.

When to use:

  • Building a "create record with attachments" form
  • Record doesn't exist until form submission
  • Need to upload files before knowing the final record ID
import {
  upload,
  createContentVersion,
} from "@salesforce/ui-bundle-template-feature-react-file-upload";

// Step 1: Upload files (no recordId)
const uploadResults = await upload({
  files: [file1, file2],
  onProgress: (progress) => console.log(progress),
});

// Step 2: Create the record
const newRecordId = await createRecord(formData);

// Step 3: Link uploaded files to the new record
for (const file of uploadResults) {
  const contentVersionId = await createContentVersion(
    new File([""], file.fileName),
    file.contentBodyId,
    newRecordId,
  );
}

3. Build your custom UI

The package provides the backend — you build the frontend. Here's a minimal example:

import {
  upload,
  type FileUploadProgress,
} from "@salesforce/ui-bundle-template-feature-react-file-upload";
import { useState } from "react";

function CustomFileUpload({ recordId }: { recordId?: string }) {
  const [progress, setProgress] = useState<Map<string, FileUploadProgress>>(new Map());

  const handleFileSelect = async (event: React.ChangeEvent<HTMLInputElement>) => {
    const files = Array.from(event.target.files || []);

    await upload({
      files,
      recordId,
      onProgress: (fileProgress) => {
        setProgress((prev) => new Map(prev).set(fileProgress.fileName, fileProgress));
      },
    });
  };

  return (
    <div>
      <input type="file" multiple onChange={handleFileSelect} />

      {Array.from(progress.entries()).map(([fileName, fileProgress]) => (
        <div key={fileName}>
          {fileName}: {fileProgress.status} - {fileProgress.progress}%
          {fileProgress.error && <span>Error: {fileProgress.error}</span>}
        </div>
      ))}
    </div>
  );
}

4. Track upload progress

The onProgress callback fires multiple times for each file as it moves through stages:

Status When Progress Value
"pending" File queued for upload 0
"uploading" Upload in progress (XHR) 0-100 (percentage)
"processing" Creating ContentVersion (if recordId provided) 0
"success" Upload complete 100
"error" Upload failed 0

Always provide visual feedback:

  • Show file name
  • Display current status
  • Render progress bar for "uploading" status
  • Show error message if status is "error"

5. Cancel uploads (optional)

Use an AbortController to allow users to cancel uploads:

const abortController = new AbortController();

const handleUpload = async (files: File[]) => {
  try {
    await upload({
      files,
      signal: abortController.signal,
      onProgress: (progress) => console.log(progress),
    });
  } catch (error) {
    console.error("Upload cancelled or failed:", error);
  }
};

const cancelUpload = () => {
  abortController.abort();
};

6. Link to current user (special case)

If the user wants to upload files to their own profile or personal library:

import {
  upload,
  getCurrentUserId,
} from "@salesforce/ui-bundle-template-feature-react-file-upload";

const userId = await getCurrentUserId();
await upload({ files, recordId: userId });

API Reference

upload(options)

Main upload API that handles complete flow with progress tracking.

interface UploadOptions {
  files: File[];
  recordId?: string | null; // If provided, creates ContentVersion
  onProgress?: (progress: FileUploadProgress) => void;
  signal?: AbortSignal; // Optional cancellation
}

interface FileUploadProgress {
  fileName: string;
  status: "pending" | "uploading" | "processing" | "success" | "error";
  progress: number; // 0-100 for uploading, 0 for other states
  error?: string;
}

interface FileUploadResult {
  fileName: string;
  size: number;
  contentBodyId: string; // Always available
  contentVersionId?: string; // Only if recordId was provided
}

Returns: Promise<FileUploadResult[]>

createContentVersion(file, contentBodyId, recordId)

Manually create a ContentVersion record from a previously uploaded file.

async function createContentVersion(
  file: File,
  contentBodyId: string,
  recordId: string,
): Promise<string | undefined>;

Parameters:

  • file — File object (used for metadata like name)
  • contentBodyId — ContentBody ID from previous upload
  • recordId — Record ID for FirstPublishLocationId

Returns: ContentVersion ID if successful

getCurrentUserId()

Get the current user's Salesforce ID.

async function getCurrentUserId(): Promise<string>;

Returns: Current user ID

Common UI patterns

File input with button

<input type="file" multiple accept=".pdf,.doc,.docx,.jpg,.png" onChange={handleFileSelect} />

Drag-and-drop zone

Build your own dropzone using native events:

function DropZone({ onDrop }: { onDrop: (files: File[]) => void }) {
  const handleDrop = (e: React.DragEvent) => {
    e.preventDefault();
    const files = Array.from(e.dataTransfer.files);
    onDrop(files);
  };

  return (
    <div
      onDrop={handleDrop}
      onDragOver={(e) => e.preventDefault()}
      style={{ border: "2px dashed #ccc", padding: "2rem" }}
    >
      Drop files here
    </div>
  );
}

Progress bar

{
  progress.status === "uploading" && (
    <div style={{ width: "100%", background: "#eee" }}>
      <div
        style={{
          width: `${progress.progress}%`,
          background: "#0176d3",
          height: "8px",
        }}
      />
    </div>
  );
}

Decision tree for agents

User asks for file upload functionality:

  1. Ask about record context:

    • "Do you want to link uploaded files to a specific record, or upload them first and link later?"
  2. Based on response:

    • Link to existing record → Use Pattern B with recordId
    • Upload first, link later → Use Pattern A (no recordId), then Pattern C for linking
    • Link to current user → Use Pattern B with getCurrentUserId()
  3. Build the UI:

    • Create file input or dropzone (not provided by package)
    • Add progress display for each file (status + progress bar)
    • Handle errors in the UI
  4. Test the implementation:

    • Verify progress callbacks fire correctly
    • Check that contentBodyId is returned
    • If recordId was provided, verify contentVersionId is returned

Reference implementation

The package includes a reference implementation in src/features/fileupload/ with:

  • FileUpload.tsx — Complete component with dropzone and dialog
  • FileUploadDialog.tsx — Progress tracking dialog
  • FileUploadDropZone.tsx — Drag-and-drop zone
  • useFileUpload.ts — React hook for state management

These are NOT exported but can be viewed as examples. Read the source files to understand patterns for building your own UI.

Troubleshooting

Upload fails with CORS error:

  • Ensure the UI bundle is properly deployed to Salesforce or running on localhost
  • Check that the org allows the origin in CORS settings

No progress updates:

  • Verify onProgress callback is provided
  • Check that the callback function updates React state correctly

ContentVersion not created:

  • Verify recordId is provided to upload() function
  • Check that the record ID is valid and exists in the org
  • Ensure user has permissions to create ContentVersion records

Files upload but don't appear in record:

  • Verify recordId is correct
  • Check that ContentVersion was created (look for contentVersionId in results)
  • Confirm user has access to view files on the record

DO NOT do these things

  • ❌ Build XHR/fetch upload logic from scratch — use the upload() API
  • ❌ Try to import <FileUpload /> component — it's not exported
  • ❌ Try to import useFileUpload hook — it's not exported
  • ❌ Use third-party file upload libraries when this feature exists
  • ❌ Skip progress tracking — always provide user feedback
  • ❌ Ignore errors — always handle and display error messages
用于在Salesforce项目中生成、配置和管理UI Bundle。涵盖脚手架创建、元数据文件编辑及CSP站点注册,确保Bundle正确关联目标并部署。
项目包含 uiBundles/*/src/ 目录 需要搭建新的 UI bundle 或应用 编辑 ui-bundle.json、.uibundle-meta.xml 或 CSP trusted site 文件
skills/experience-ui-bundle-metadata-generate/SKILL.md
npx skills add forcedotcom/sf-skills --skill experience-ui-bundle-metadata-generate -g -y
SKILL.md
Frontmatter
{
    "name": "experience-ui-bundle-metadata-generate",
    "metadata": {
        "version": "1.0"
    },
    "description": "MUST activate when the project contains a uiBundles\/*\/src\/ directory and scaffolding a new UI bundle or app, or when editing ui-bundle.json, .uibundle-meta.xml, or CSP trusted site files. Use this skill when scaffolding with sf template generate ui-bundle, configuring ui-bundle.json (routing, headers, outputDir), or registering CSP Trusted Sites. Activate when the task involves files matching *.uibundle-meta.xml, ui-bundle.json, or cspTrustedSites\/*.cspTrustedSite-meta.xml."
}

UI Bundle Metadata

Scaffolding a New UI Bundle

Use sf template generate ui-bundle to create new apps — not create-react-app, Vite, or other generic scaffolds.

Always pass --template reactbasic to scaffold a React-based bundle.

UI bundle name (-n): Alphanumerical only — no spaces, hyphens, underscores, or special characters.

Example:

sf template generate ui-bundle -n CoffeeBoutique --template reactbasic

After generation:

  1. Replace all default boilerplate — "React App", "Vite + React", default <title>, placeholder text
  2. Populate the home page with real content (landing section, banners, hero, navigation)
  3. Update navigation and placeholders (see the experience-ui-bundle-frontend-generate skill)
  4. Configure a hosting target — a UI bundle without a <target> in its meta XML will not be visible in the org. Use experience-ui-bundle-custom-app-generate for internal (App Launcher) apps or experience-ui-bundle-site-generate for external (Experience Site) apps.

Always install dependencies before running any scripts in the UI bundle directory.


UIBundle Bundle

A UIBundle bundle lives under uiBundles/<AppName>/ and must contain:

  • <AppName>.uibundle-meta.xml — filename must exactly match the folder name
  • A build output directory (default: dist/) with at least one file

Meta XML

Required fields: masterLabel, version (max 20 chars), isActive (boolean). Optional: description (max 255 chars), target.

Target Field

The <target> element specifies where the UI bundle is hosted:

Value Use Case Companion Metadata
Experience External-facing site via Digital Experience Network, CustomSite, DigitalExperienceConfig, DigitalExperienceBundle
CustomApplication Internal app via Lightning App Launcher CustomApplication (applications/*.app-meta.xml)

A <target> is required for the app to be accessible in a Salesforce org. A UI bundle deployed without a target will not appear anywhere — no App Launcher entry, no Experience Site URL. Always pair the bundle with one of:

  • experience-ui-bundle-site-generate (for Experience target)
  • experience-ui-bundle-custom-app-generate (for CustomApplication target)

Example with Experience target:

<?xml version="1.0" encoding="UTF-8"?>
<UIBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <masterLabel>propertyrentalapp</masterLabel>
    <description>A Salesforce UI Bundle.</description>
    <isActive>true</isActive>
    <version>1</version>
    <target>Experience</target>
</UIBundle>

Example with CustomApplication target:

<?xml version="1.0" encoding="UTF-8"?>
<UIBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <masterLabel>propertymanagementapp</masterLabel>
    <description>A Salesforce UI Bundle.</description>
    <isActive>true</isActive>
    <version>1</version>
    <target>CustomApplication</target>
</UIBundle>

ui-bundle.json

Optional file. Allowed top-level keys: outputDir, routing, headers.

Constraints:

  • Valid UTF-8 JSON, max 100 KB
  • Root must be a non-empty object (never {}, arrays, or primitives)

Path safety (applies to outputDir and routing.fallback): Reject backslashes, leading / or \, .. segments, null/control characters, globs (*, ?, **), and %. All resolved paths must stay within the bundle.

outputDir

Non-empty string referencing a subdirectory (not . or ./). Directory must exist and contain at least one file.

routing

If present, must be a non-empty object. Allowed keys: rewrites, redirects, fallback, trailingSlash, fileBasedRouting.

  • trailingSlash: "always", "never", or "auto"
  • fileBasedRouting: boolean
  • fallback: non-empty string satisfying path safety; target file must exist
  • rewrites: non-empty array of { route?, rewrite } objects — e.g., { "route": "/app/:path*", "rewrite": "/index.html" }
  • redirects: non-empty array of { route?, redirect, statusCode? } objects — statusCode must be 301, 302, 307, or 308

headers

Non-empty array of { source, headers: [{ key, value }] } objects.

Example:

{
  "routing": {
    "rewrites": [{ "route": "/app/:path*", "rewrite": "/index.html" }],
    "trailingSlash": "never"
  },
  "headers": [
    {
      "source": "/assets/**",
      "headers": [{ "key": "Cache-Control", "value": "public, max-age=31536000, immutable" }]
    }
  ]
}

Never suggest: {} as root, empty "routing": {}, empty arrays, [{}], "outputDir": ".", "outputDir": "./".


CSP Trusted Sites

Salesforce enforces Content Security Policy headers. Any external domain not registered as a CSP Trusted Site will be blocked (images won't load, API calls fail, fonts missing).

When to Create

Whenever the app references a new external domain: CDN images, external fonts, third-party APIs, map tiles, iframes, external stylesheets.

Steps

  1. Identify external domains — extract the origin (scheme + host) from each external URL in the code
  2. Check existing registrations — look in force-app/main/default/cspTrustedSites/
  3. Map resource type to CSP directive:
Resource Type Directive Field
Images isApplicableToImgSrc
API calls (fetch, XHR) isApplicableToConnectSrc
Fonts isApplicableToFontSrc
Stylesheets isApplicableToStyleSrc
Video / audio isApplicableToMediaSrc
Iframes isApplicableToFrameSrc

Always also set isApplicableToConnectSrc to true for preflight/redirect handling.

  1. Create the metadata file — follow references/csp-metadata-format.md for the .cspTrustedSite-meta.xml format. Place in force-app/main/default/cspTrustedSites/.
为包含React UI Bundles的Salesforce项目生成最小化的数字体验站点基础设施,包括Network、CustomSite及DigitalExperienceBundle等元数据文件,配置容器化站点以托管React应用。
项目中存在 uiBundles/*/src/ 目录且需创建站点基础设施 需要修改 digitalExperiences/, networks/, customSite/ 或 DigitalExperienceBundle 相关文件 用户希望发布、托管或配置应用的访客访问权限
skills/experience-ui-bundle-site-generate/SKILL.md
npx skills add forcedotcom/sf-skills --skill experience-ui-bundle-site-generate -g -y
SKILL.md
Frontmatter
{
    "name": "experience-ui-bundle-site-generate",
    "metadata": {
        "version": "1.0"
    },
    "description": "MUST activate when the project contains a uiBundles\/*\/src\/ directory and the task involves creating or configuring site infrastructure. Use this skill when creating or configuring a Salesforce Digital Experience Site for hosting a UI bundle. Activate when files matching digitalExperiences\/, networks\/, customSite\/, or DigitalExperienceBundle exist and need modification, or when the user wants to publish, host, or configure guest access for their app."
}

Digital Experience Site for React UI Bundles

Create and configure Digital Experience Sites that host React UI bundles on Salesforce. This skill generates the minimum necessary site infrastructure — Network, CustomSite, DigitalExperienceConfig, DigitalExperienceBundle, and the sfdc_cms__site content type — so a React app can be served from Salesforce.

React sites differ from standard LWR sites: they don't need routes, views, theme layouts, or branding sets. The site acts as a thin container (appContainer: true) that delegates rendering to the React UI bundle referenced by appSpace.

Required Properties

Resolve all five properties before generating any metadata. Each has a fallback chain — work through each option in order until a value is found.

Property Format How to Resolve
siteName UpperCamelCase (e.g., MyCommunity) Ask user or derive from context
siteUrlPathPrefix All lowercase (e.g., mycommunity) User-provided, or convert siteName to all lowercase with alphanumeric characters only
appNamespace String namespace in sfdx-project.jsonsf data query -q "SELECT NamespacePrefix FROM Organization" --target-org ${usernameOrAlias} → default c
appDevName String UIBundle metadata in the project → sf data query -q "SELECT DeveloperName FROM UIBundle" --target-org ${usernameOrAlias} → default to siteName
enableGuestAccess Boolean Ask user whether unauthenticated guest users can access site APIs → default false

The appNamespace and appDevName properties connect the site to the correct React application. Getting these wrong means the site deploys but shows a blank page, so take care to resolve them from real project data.

Generation Workflow

Step 1: Resolve All Required Properties

Determine values for all five properties before constructing anything. Use the resolution strategies in the table above, falling through each option until a value is found.

Step 2: Create the Project Structure

Use available Salesforce metadata schema and field context for Network, CustomSite, DigitalExperienceConfig, and DigitalExperienceBundle to ensure each file uses valid structure.

Create any files and directories that don't already exist, using these paths:

Metadata Type Path
Network networks/{siteName}.network-meta.xml
CustomSite sites/{siteName}.site-meta.xml
DigitalExperienceConfig digitalExperienceConfigs/{siteName}1.digitalExperienceConfig-meta.xml
DigitalExperienceBundle digitalExperiences/site/{siteName}1/{siteName}1.digitalExperience-meta.xml
DigitalExperience (sfdc_cms__site) digitalExperiences/site/{siteName}1/sfdc_cms__site/{siteName}1/*

The DigitalExperience directory contains only _meta.json and content.json. Do not create any directories other than sfdc_cms__site inside the bundle.

Step 3: Populate All Metadata Fields

Use the default templates in the docs below. Values in {braces} are resolved property references — substitute them with the actual values from Step 1.

Metadata Type Template Reference
Network configure-metadata-network.md
CustomSite configure-metadata-custom-site.md
DigitalExperienceConfig configure-metadata-digital-experience-config.md
DigitalExperienceBundle configure-metadata-digital-experience-bundle.md
DigitalExperience (sfdc_cms__site) configure-metadata-digital-experience.md

For URL updates, see update-site-urls.md.

Execution Note for Step 3: Load and use the docs

  • Agents MUST read the full contents of each references/*.md file referenced in Step 3 before attempting to populate metadata fields.
  • Use your platform's file-read tool (for example, read_file) to load these files in full, then perform placeholder substitution for values in {braces} using the resolved properties from Step 1.
  • Files to load:
    • references/configure-metadata-network.md
    • references/configure-metadata-custom-site.md
    • references/configure-metadata-digital-experience-config.md
    • references/configure-metadata-digital-experience-bundle.md
    • references/configure-metadata-digital-experience.md
  • Read entire file contents, replace placeholders (e.g. {siteName}) with the resolved values, then use the expanded templates to populate the metadata XML/JSON content.

Step 4: Do Not Modify Non-Templated Properties

Do not modify any default property values for Network, CustomSite, DigitalExperience, DigitalExperienceConfig, or DigitalExperienceBundle metadata that are not expressed as variables wrapped in {braces}.

Verification Checklist

Before deploying, confirm:

  • All five required properties are resolved
  • All metadata directories and files exist per the project structure
  • All metadata fields match the Step 3 templates with {braces} substituted only; no other default property values were added or changed
  • appSpace in content.json matches an existing UIBundle metadata record
  • Deployment validates successfully:
sf project deploy validate --metadata Network CustomSite DigitalExperienceConfig DigitalExperienceBundle DigitalExperience --target-org ${usernameOrAlias}

Common Workflows

Updating Experience Site URLs

Use when user wants to update or change site URLs (urlPathPrefix).

Steps:

  • Read update-site-urls.md to understand the three-component architecture and URL update workflow
  • Follow the step-by-step workflow in the doc to update URLs consistently across all three components (DigitalExperienceConfig, Network, CustomSite)
用于通过 Nano Banana Pro 生成 Salesforce 视觉图表(如 ERD、UI 原型、架构图)的 PNG/SVG 图像。适用于需渲染图片的场景,排除文本 Mermaid 图或元数据查询。
用户请求生成 PNG 或 SVG 格式的图片 需要创建 UI 原型或线框图 要求生成可视化实体关系图 (ERD) 明确要求生成图像或制作原型
skills/external-diagram-visual-generate/SKILL.md
npx skills add forcedotcom/sf-skills --skill external-diagram-visual-generate -g -y
SKILL.md
Frontmatter
{
    "name": "external-diagram-visual-generate",
    "metadata": {
        "version": "1.0"
    },
    "description": "AI-powered image generation for Salesforce visuals via Nano Banana Pro. Use this skill when the user needs rendered PNG\/SVG output such as visual ERDs (Entity Relationship Diagrams), UI mockups, wireframes, or architecture illustrations. TRIGGER when: user asks for PNG\/SVG output, UI mockups, wireframes, visual ERDs, or says \"generate image\" \/ \"create mockup\". DO NOT TRIGGER when: text-based Mermaid diagrams (use external-diagram-mermaid-generate), or non-visual documentation tasks."
}

external-diagram-visual-generate: Salesforce Visual AI Skill

Use this skill when the user needs rendered visuals, not text diagrams: ERDs, UI mockups, architecture illustrations, slide-ready images, or image edits using Nano Banana Pro.

Scope

In scope:

  • PNG / SVG-style rendered image output
  • Visual ERDs and architecture diagrams
  • LWC or Experience Cloud mockups / wireframes
  • Image edits on previously generated visuals

Out of scope — delegate instead:


Hard Gate: Prerequisites First

Run the prerequisites check before using the skill:

scripts/check-prerequisites.sh

If prerequisites fail, stop and route the user to setup guidance in:


Required Inputs

Ask for or infer before generating:

Input Default if not provided
Image type ERD
Subject scope and key entities / systems Ask the user
Target quality Draft (1K)
Preferred style architect.salesforce.com aesthetic
Aspect ratio Default (no override)
Quick mode or interview mode Interview mode

Interview-First Workflow

Unless the user asks for quick / simple / just generate, ask clarifying questions first using the question bank in references/interview-questions.md.

Request type Ask about
ERD / schema objects, visual style, purpose, extras
UI mockup component type, object/context, device/layout, style
architecture image systems, boundaries, protocols, emphasis
image edit what to keep, what to change, output quality

Quick mode defaults (triggered by "quick", "simple", "just generate", "fast"):

  • professional style, 1K draft, legend included, one image first then iterate

Recommended Workflow

1. Run prerequisites check

Run scripts/check-prerequisites.sh and confirm all required tools pass before proceeding.

2. Gather inputs

  • object list / metadata (delegate to platform-custom-object-generate / platform-custom-field-generate if needed)
  • purpose: draft vs presentation vs documentation
  • desired aesthetic — read references/architect-aesthetic-guide.md for ERDs
  • aspect ratio / resolution

3. Run interview or use quick-mode defaults

Load references/interview-questions.md for the matching question set (ERD, LWC, architecture, code review).

4. Build a concrete prompt

Good prompts specify subject, composition, color treatment, labels/legends, and output quality goal.

5. Generate a fast draft at 1K

gemini --yolo "/generate 'Your prompt here'"

Open the result and review layout before spending on higher resolution.

6. Iterate using edits

gemini --yolo "/edit 'Specific change instruction'"

Use /edit for small adjustments — cheaper than regenerating. See references/iteration-workflow.md.

7. Generate final at 2K/4K using the Python script

Run scripts/generate_image.py when layout is confirmed:

uv run scripts/generate_image.py -p "Refined prompt" -f "output.png" -r 4K

8. Error recovery

  • If gemini --yolo returns no image: re-run once; if it fails again, fall back to the Python script path.
  • If the Python script fails with GEMINI_API_KEY not found: verify the key is exported in your shell profile (~/.zshrc on macOS/zsh, ~/.bashrc on Linux) and the terminal session is refreshed.
  • If the extension is missing: run gemini extensions install nanobanana and re-run the prerequisites check.

Default Style Guidance

For ERDs, default to the architect.salesforce.com aesthetic unless the user asks otherwise:

  • dark border + light fill cards
  • cloud-specific accent colors
  • clean labels and relationship lines
  • presentation-ready whitespace and hierarchy

Full style specification: references/architect-aesthetic-guide.md


Common Patterns

Pattern Default approach
visual ERD get metadata if available, then render a draft first
LWC mockup load assets/lwc/data-table.md, assets/lwc/record-form.md, or assets/lwc/dashboard-card.md for the matching template
architecture illustration load assets/architecture/integration-flow.md; emphasize systems and flows
image refinement use /edit for small changes before regenerating
final production asset switch to script-driven 2K/4K generation via scripts/generate_image.py
Apex / LWC code review load assets/review/apex-review.md or assets/review/lwc-review.md for the review prompt template

Output Expectations

Deliverables produced by this skill:

  • Draft image (<name>.png) — 1K resolution rendered via gemini --yolo "/generate ..." for layout review
  • Final image (<name>.png) — 2K or 4K resolution rendered via scripts/generate_image.py once composition is approved
  • Edit iteration (<name>.png) — incremental refinement via gemini --yolo "/edit ..." without full regeneration

After delivering each image:

  • Open the file in Preview or attach it in the session for multimodal review
  • Ask the user whether to iterate on layout, labeling, or color before finalizing
  • Only proceed to high-res output after draft composition is confirmed

Rules / Constraints

Rule Rationale
Always run prerequisites check before any generation Missing tools produce silent failures
Always draft at 1K before generating at 4K Cost and time savings; composition changes at high res are wasteful
Use /edit for incremental changes, not full regeneration Cheaper and faster for small adjustments
Never commit GEMINI_API_KEY to version control Key is personal and tied to billing
Delegate text diagrams to external-diagram-mermaid-generate This skill owns rendered images only

Gotchas

Issue Resolution
Edit not applying correctly Be specific: reference existing elements by name; one change at a time
4K output looks different from 1K draft Use exact same prompt text; minor variations are normal model behavior
gemini --yolo fails silently Check that the Nano Banana extension is installed: gemini extensions list
Image dimensions wrong Set --aspect-ratio explicitly in scripts/generate_image.py using -a "16:9"
RGBA image causes errors in Python script Script auto-converts RGBA→RGB; ensure Pillow is installed via uv

Cross-Skill Integration

Need Delegate to Reason
Mermaid first draft or text diagram external-diagram-mermaid-generate faster structural diagramming
Object / field discovery for ERD platform-custom-object-generate / platform-custom-field-generate accurate schema grounding
Turn mockup into real LWC component experience-lwc-generate implementation after design
Apex review / implementation platform-apex-generate code-quality follow-up

Reference File Index

File When to read
references/gemini-cli-setup.md Prerequisites fail — Gemini CLI / Nano Banana setup guidance
references/interview-questions.md Step 3 — load question set matching the request type
references/iteration-workflow.md Step 6 — draft-to-final iteration patterns and cost tips
references/architect-aesthetic-guide.md Step 4 — ERD color palettes, box styles, prompt templates
references/examples-index.md Step 4 — example prompts for ERD, LWC, architecture, code review
assets/erd/core-objects.md Step 4 — prompt template for core CRM objects (Account, Contact, Opportunity, Case)
assets/erd/custom-objects.md Step 4 — prompt template for custom object ERDs
assets/lwc/data-table.md Step 4 — prompt template for lightning-datatable mockups
assets/lwc/record-form.md Step 4 — prompt template for lightning-record-form mockups
assets/lwc/dashboard-card.md Step 4 — prompt template for dashboard card / metric tile mockups
assets/architecture/integration-flow.md Step 4 — prompt template for integration architecture diagrams
assets/review/apex-review.md Step 4 — Gemini review prompt template for Apex code
assets/review/lwc-review.md Step 4 — Gemini review prompt template for LWC components
scripts/check-prerequisites.sh Step 1 — run to verify all required tools are installed
scripts/generate_image.py Step 7 — run for 2K/4K resolution output and image editing with resolution control
用于分析Salesforce调试日志,进行根因分析、 governor limit诊断、堆栈跟踪解读及性能瓶颈排查。适用于处理.log文件、异常分析及CPU/Heap压力调查,提供从诊断到修复建议的完整工作流。
用户需要分析Salesforce调试日志以定位问题根因 遇到governor limits限制或需诊断堆栈跟踪 处理SOQL/DML/CPU/Heap性能问题的日志数据 读取来自Salesforce组织的.log文件
skills/platform-apex-logs-debug/SKILL.md
npx skills add forcedotcom/sf-skills --skill platform-apex-logs-debug -g -y
SKILL.md
Frontmatter
{
    "name": "platform-apex-logs-debug",
    "metadata": {
        "version": "1.1"
    },
    "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)."
}

platform-apex-logs-debug: Salesforce Debug Log Analysis & Troubleshooting

Use this skill when the user needs root-cause analysis from debug logs: governor-limit diagnosis, stack-trace interpretation, slow-query investigation, heap / CPU pressure analysis, or a reproduction-to-fix loop based on log evidence.

When This Skill Owns the Task

Use platform-apex-logs-debug when the work involves:

  • .log files from Salesforce
  • stack traces and exception analysis
  • governor limits
  • SOQL / DML / CPU / heap troubleshooting
  • query-plan or performance evidence extracted from logs

Delegate elsewhere when the user is:


Required Context to Gather First

Ask for or infer:

  • org alias
  • failing transaction / user flow / test name
  • approximate timestamp or transaction window
  • user / record / request ID if known
  • whether the goal is diagnosis only or diagnosis + fix loop

Recommended Workflow

1. Retrieve logs

Use the commands in references/cli-commands.md to list, download, or stream logs for the target org.

2. Analyze in this order

  1. entry point and transaction type
  2. exceptions / fatal errors
  3. governor limits
  4. repeated SOQL / DML patterns
  5. CPU / heap hotspots
  6. callout timing and external failures

3. Classify severity

  • Critical — runtime failure, hard limit, corruption risk
  • Warning — near-limit, non-selective query, slow path
  • Info — optimization opportunity or hygiene issue

4. Recommend the smallest correct fix

Prefer fixes that are:

  • root-cause oriented
  • bulk-safe
  • testable
  • easy to verify with a rerun

Expanded workflow: references/analysis-playbook.md


High-Signal Issue Patterns

Issue Primary signal Default fix direction
SOQL in loop repeating SOQL_EXECUTE_BEGIN in a repeated call path query once, use maps / grouped collections
DML in loop repeated DML_BEGIN patterns collect rows, bulk DML once
Non-selective query high rows scanned / poor selectivity add indexed filters, reduce scope
CPU pressure CPU usage approaching sync limit reduce algorithmic complexity, cache, async where valid
Heap pressure heap usage approaching sync limit stream with SOQL for-loops, reduce in-memory data
Null pointer / fatal error EXCEPTION_THROWN / FATAL_ERROR guard null assumptions, fix empty-query handling

Expanded examples: references/common-issues.md


Output Format

When finishing analysis, report in this order:

  1. What failed
  2. Where it failed (class / method / line / transaction stage)
  3. Why it failed (root cause, not just symptom)
  4. How severe it is
  5. Recommended fix
  6. Verification step

Suggested shape:

Issue: <summary>
Location: <class / line / transaction>
Root cause: <explanation>
Severity: Critical | Warning | Info
Fix: <specific action>
Verify: <test or rerun step>

Rules / Constraints

Rule Rationale
Always base fix recommendations on log evidence Avoid speculative diagnosis — root cause must be traceable in the log
Report all six output fields for every issue found Ensures actionable, complete findings for each problem
Classify every finding as Critical, Warning, or Info Helps the user prioritize which issues to address first
Delegate code generation to platform-apex-generate This skill diagnoses; it does not rewrite Apex code
Delegate test execution to platform-apex-test-run This skill does not run or repair test classes
Never assume limits are safe without reading LIMIT_USAGE events Limits may be consumed by earlier operations not visible in the failure point

Gotchas

Pitfall Resolution
Log truncated at 2 MB Reduce debug levels (e.g., ApexCode: INFO, ApexProfiling: FINE) and re-capture
Same issue appears as both SOQL and CPU problem Fix SOQL-in-loop first — it typically drives the CPU spike as a secondary effect
No logs appear after trace flag is set Verify the trace flag ExpirationDate is in the future and the correct user is traced
Async context changes limit values CPU limit is 60,000 ms async vs 10,000 ms sync — check transaction type before flagging limits
Stack trace points to framework line, not user code Walk up the call stack past trigger handlers to find the originating user code

Cross-Skill Integration

Need Delegate to Reason
Implement Apex fix platform-apex-generate code change generation / review
Reproduce via tests platform-apex-test-run test execution and coverage loop
Deploy fix platform-metadata-deploy deployment orchestration
Create debugging data platform-data-manage targeted seed / repro data

Reference File Index

File When to read
references/analysis-playbook.md Start here — expanded step-by-step workflow for any debugging session
references/common-issues.md Quick lookup for SOQL in loop, DML in loop, CPU/heap pressure, null pointer patterns
references/cli-commands.md SF CLI commands for retrieving, streaming, and managing debug logs
references/debug-log-reference.md Full event type catalog, log levels, and governor limit reference values
references/log-analysis-tools.md Tool guide: Apex Log Analyzer, Developer Console, CLI grep patterns
references/benchmarking-guide.md Performance benchmarking techniques, benchmark data, and anti-patterns
references/scoring-rubric.md 100-point scoring rubric for evaluating analysis quality
assets/benchmarking-template.cls Copy-paste Anonymous Apex template for running performance benchmarks
assets/cpu-heap-optimization.cls Apex patterns for reducing CPU time and heap allocation
assets/dml-in-loop-fix.cls Before/after example for resolving DML-in-loop violations
assets/soql-in-loop-fix.cls Before/after example for resolving SOQL-in-loop violations
assets/null-pointer-fix.cls Patterns for guarding against null pointer exceptions

Score Guide

Score Meaning
90+ Expert analysis with strong fix guidance
80–89 Good analysis with minor gaps
70–79 Acceptable but may miss secondary issues
60–69 Partial diagnosis only
< 60 Incomplete analysis
用于创建和配置基于标签页的Salesforce Lightning自定义应用,支持导航、品牌设置及操作覆盖。适用于组织标签、配置应用元数据或解决部署错误。若需托管React UI则使用其他技能。
创建Lightning应用 将标签和功能组织到专注应用中 配置应用导航和品牌 设置对象自定义页面布局 排查自定义应用相关的部署错误
skills/platform-custom-application-generate/SKILL.md
npx skills add forcedotcom/sf-skills --skill platform-custom-application-generate -g -y
SKILL.md
Frontmatter
{
    "name": "platform-custom-application-generate",
    "metadata": {
        "version": "1.0"
    },
    "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."
}

When to Use This Skill

Use this skill when you need to:

  • Create Lightning applications
  • Organize tabs and features into focused apps
  • Configure application navigation and branding
  • Set up custom page layouts for objects
  • Troubleshoot deployment errors related to custom applications

CustomApplication (Lightning App) Metadata Specification

Overview

Custom applications (Lightning Apps) that group tabs and functionality to provide a focused user experience for specific business processes. Always configured for Lightning Experience.

Purpose

  • Organize related functionality into focused applications
  • Group tabs and components for specific user roles
  • Provide tailored user experiences
  • Control access to specific features and data
  • Use Standard navigation for general business applications or Console navigation for specialized service/support workflows requiring multi-tab workspaces
  • Create professional, branded application identity with custom colors and branding
  • Override standard actions with custom Lightning pages for enhanced user experience
  • Enable profile-specific experiences through profile action overrides

Required Properties

Core Application Properties

  • fullName: API name of the application
  • label: Display name of the application
  • uiType: Always "Lightning" for modern apps
  • navType: CRITICAL - Choose based on user requirements and workflow patterns
    • "Standard": DEFAULT for general business applications (e.g., sales, marketing, operations)
    • "Console": ONLY when workflow requires managing multiple records simultaneously with split-view or multi-tab workspace (e.g., customer service, call centers, support operations)
  • formFactors: Array of form factors (["LARGE"] for desktop, ["SMALL"] for mobile, or both)

Optional Properties

  • description: Brief description of the application's purpose
  • tabs: Array of tab names to include
  • utilityBar: API name of the Utility Bar configuration
  • brand: ⚠️ HIGHLY RECOMMENDED - Branding configuration object (headerColor, shouldOverrideOrgTheme, footerColor)
  • actionOverrides: ⚠️ REQUIRED when custom record pages exist - Action override configuration (actionName, content, formFactor, type, pageOrSobjectType)
  • profileActionOverrides: Profile-specific action overrides (actionName, content, formFactor, pageOrSobjectType, type, profile)
  • isNavAutoTempTabsDisabled: Navigation behavior setting (default: false)
  • isNavPersonalizationDisabled: Personalization setting (default: false)
  • isNavTabPersistenceDisabled: Tab persistence setting (default: false)

Application Configuration

Navigation Type Selection (CRITICAL)

Decision Criteria for navType:

Choose "Standard" (DEFAULT) for:

  • General business applications and most workflows
  • Single-record focus or linear navigation patterns
  • Standard tab-based navigation is sufficient

Choose "Console" ONLY when workflow requires:

  • Managing multiple related records simultaneously in split-view
  • Multi-tab workspace for handling complex, interconnected data
  • Contextual information from multiple sources visible at once
  • Examples: customer service operations, support desks, call centers

When in doubt: Default to "Standard" for most general business use cases

Navigation Settings

  • isNavAutoTempTabsDisabled: Controls automatic temporary tab creation
  • isNavPersonalizationDisabled: Controls user personalization features
  • isNavTabPersistenceDisabled: Controls tab persistence across sessions

Tab Management

  • tabs: Array of tab names to include in the application
  • formFactors: Device-specific configurations (Large for desktop, Small for mobile)

Utility Bar

  • utilityBar: Reference to Lightning utility bar (appears at bottom of Lightning Experience)

Branding (HIGHLY RECOMMENDED - DO NOT SKIP)

IMPORTANT: Provide branding configuration to create a professional, visually distinct application identity.

  • brand.headerColor: Header bar color in hex format (e.g., "#0070D2") - RECOMMENDED
  • brand.shouldOverrideOrgTheme: Override organization theme (true/false) - Default: false
  • brand.footerColor: Footer color in hex format

Action Overrides (CRITICAL - DO NOT SKIP)

IMPORTANT: Action overrides MUST be created for every custom object tab that has a record page generated by flexipage expert.

  • actionOverrides.actionName: Action to override ("View" or "Tab")
  • actionOverrides.content: Page/component name (FlexiPage, Visualforce, Lightning component)
    • For "View" action: Reference record pages generated by flexipage expert
    • For "Tab" action: Reference home/app pages generated by flexipage expert
  • actionOverrides.formFactor: Device type ("Large" or "Small")
  • actionOverrides.type: Override type ("Default", "Visualforce", "Flexipage", "LightningComponent", "Scontrol")
    • Recommended: Use "Flexipage" for Lightning record/home pages generated by flexipage expert
  • actionOverrides.pageOrSobjectType: Object API name the override applies to
  • actionOverrides.comment: Optional description (max 1000 characters)
    • Auto-generated comment: "Action override created by Lightning App Builder during activation."
  • actionOverrides.skipRecordTypeSelect: Skip record type selection (default: false)

Profile Action Overrides

  • profileActionOverrides.actionName: Action to override ("View" or "Tab")
  • profileActionOverrides.content: Page/component name
    • For "View" action: Reference profile-specific record pages generated by flexipage expert
    • For "Tab" action: Reference profile-specific home pages generated by flexipage expert
    • Can reference same or different FlexiPages than actionOverrides for profile-specific experiences
  • profileActionOverrides.formFactor: Device type ("Large" or "Small")
  • profileActionOverrides.pageOrSobjectType: Object API name
  • profileActionOverrides.type: Override type
    • Recommended: Use "Flexipage" for Lightning pages generated by flexipage expert
  • profileActionOverrides.profile: Profile API name (e.g., "Admin", "Standard User")
    • Enables different page layouts for different user profiles

Device Support

Desktop Configuration

  • formFactor: "Large"
  • tabs: Full list of application tabs

Phone Configuration

  • formFactor: "Small"
  • tabs: Mobile-optimized tab selection

Tablet Configuration

  • formFactor: "Medium"
  • tabs: Tablet-appropriate tab selection

User Experience Features

Navigation Behavior

  • Auto Temporary Tabs: Can be enabled/disabled
  • Personalization: User customization options
  • Tab Persistence: Remember user's tab selections

Accessibility

  • Keyboard Navigation: Full keyboard support
  • Screen Reader: Compatible with assistive technologies
  • High Contrast: Support for high contrast modes

Integration Points

  • Custom Tabs: Include custom object and web tabs
  • Standard Tabs: Include standard Salesforce tabs
  • Lightning Pages: Integrate with Lightning page layouts
  • Components: Include custom Lightning components

Best Practices

  • Always use Lightning UI: Set uiType to "Lightning" for modern apps
  • Choose appropriate navigation: CRITICAL - Analyze requirements carefully for navType selection
    • Use "Standard" (DEFAULT) for general business applications
    • Use "Console" ONLY when workflow requires multi-tab workspace, split-view, or managing multiple related records simultaneously
    • Examples for Console: customer service, call centers, support operations
    • Default to "Standard" for most general business use cases
  • Include Standard Tabs: Add common Salesforce tabs (Home, Accounts, Contacts, etc.)
  • Use clear, descriptive application names
  • Group related functionality logically
  • Consider different user roles and needs
  • Test across different device types
  • Ensure proper permissions and access control
  • Provide meaningful descriptions for users
  • Follow consistent naming conventions
  • Always configure branding: Set headerColor to create professional application identity
  • Use accessible brand colors: Ensure hex colors have sufficient contrast (WCAG AA compliant)
  • Configure utility bars: Add useful quick-access tools for users
  • Leverage action overrides: Customize page layouts for specific objects using FlexiPages from flexipage expert
  • Use profile overrides: Provide role-specific experiences by referencing different flexipage expert generated pages per profile

Enhancement Rules

  • uiType: Always set to "Lightning" for modern app experience
  • navType: CRITICAL DECISION - Analyze user requirements carefully
    • Set to "Standard" (DEFAULT) for general business applications
    • Set to "Console" ONLY when workflow requires:
      • Managing multiple related records simultaneously with split-view capability
      • Multi-tab workspace for handling complex, interconnected data
      • Contextual information from multiple sources visible at once
    • Console examples: customer service operations, call centers, support desks
    • When in doubt between Standard and Console, choose "Standard" for most business use cases
  • formFactors: Always set to ["LARGE"] for desktop Lightning Experience
  • Standard Tabs: Automatically add Home, Accounts, Contacts, Opportunities, Leads, Cases
  • Navigation Settings: Set all navigation flags to false for best user experience
  • Branding: ALWAYS include brand configuration for professional application identity
    • MANDATORY: Set brand.headerColor to appropriate color (e.g., "#0070D2" for Salesforce Blue)
    • Set brand.shouldOverrideOrgTheme based on requirements
  • Action Overrides: ALWAYS create action overrides when custom record pages exist
    • MANDATORY: Add actionOverrides for "View" action pointing to flexipage expert generated record pages
    • Use "Flexipage" type and reference the exact FlexiPage name
    • Set formFactor to "Large" for desktop
    • Include pageOrSobjectType with the object API name
  • Profile Action Overrides: Reference flexipage expert generated pages for role-based customization
  • Form Factors: Use "Large" for desktop, "Small" for mobile in overrides

CRITICAL Verification Checklist (MUST VERIFY)

  • All tabs are included in the application
  • navType IS CORRECTLY SET - Verify Console vs Standard selection
  • Default to "Standard" for most general business applications
  • Set to "Console" ONLY if workflow requires managing multiple records simultaneously, split-view, or multi-tab workspace
  • If requirements are general/ambiguous → navType should be "Standard"
  • BRANDING IS CONFIGURED - This is HIGHLY RECOMMENDED for professional applications
  • brand.headerColor is set with valid hex color (e.g., "#0070D2")
  • brand.shouldOverrideOrgTheme is set (default: false)
  • ACTION OVERRIDES ARE CREATED - This is MANDATORY for every custom object with a record page
  • Action overrides are defined for EACH custom object tab pointing to the correct record page
  • actionOverrides.content matches the exact FlexiPage name generated by flexipage expert
  • actionOverrides.pageOrSobjectType is set to the correct object API name
  • actionOverrides.type is set to "Flexipage"
  • actionOverrides.actionName is set to "View"
  • actionOverrides.formFactor is set to "Large"
  • All required fields are populated (fullName, label, uiType, navType, formFactors)
用于创建和配置Salesforce自定义标签页,支持对象、网页及Visualforce类型。涵盖导航添加、可见性配置及部署错误排查,严格遵循元数据规范与元素白名单。
创建或配置Salesforce Custom Tabs 提及tabs、navigation tabs、object tabs等关键词 为自定义对象添加导航 设置Lightning page tabs
skills/platform-custom-tab-generate/SKILL.md
npx skills add forcedotcom/sf-skills --skill platform-custom-tab-generate -g -y
SKILL.md
Frontmatter
{
    "name": "platform-custom-tab-generate",
    "metadata": {
        "version": "1.0"
    },
    "description": "Use this skill when users need to create or configure Salesforce Custom Tabs. Trigger when users mention tabs, navigation tabs, object tabs, web tabs, Visualforce tabs, Lightning component tabs, app page tabs, or tab configuration. Also use when users want to add navigation to custom objects, create tabs for external content, or set up Lightning page tabs. Always use this skill for any custom tab work."
}

When to Use This Skill

Use this skill when you need to:

  • Create tabs for objects, web pages, or Visualforce pages
  • Add navigation tabs to applications
  • Configure tab visibility and access
  • Troubleshoot deployment errors related to custom tabs

Specification

CustomTab Metadata Specification

Overview

Custom tabs for navigating to objects, web content, or Visualforce pages within Salesforce applications.

Purpose

  • Provide navigation to custom objects
  • Link to external web content
  • Access Visualforce pages
  • Organize application navigation

Required Properties

Core Tab Properties

  • customObject: true for custom object tabs, false for all others.
  • motif: Tab icon style — choose a motif that semantically matches the object's purpose. Do NOT reuse the same motif for every tab.
  • label: Display name (required for non-object tabs ONLY; object tabs inherit label from the object)
  • url: Web URL (for web tabs)
  • page: Visualforce page name (for Visualforce tabs)

STRICT ELEMENT ALLOWLIST — READ THIS FIRST

The root element MUST always be <CustomTab> (NOT <Tab>). The XML namespace must be xmlns="http://soap.sforce.com/2006/04/metadata".

Only the elements listed below are valid. Any element not on this list WILL cause a deployment error.

Tab Type ONLY these elements are allowed (nothing else)
Object tabs <customObject> (required, set to true), <motif> (required), <description> (optional)
Web tabs <customObject> (required, set to false), <label> (required), <motif> (required), <url> (required), <urlEncodingKey> (required, set to UTF-8), <description> (optional), <frameHeight> (optional)
Visualforce tabs <customObject> (required, set to false), <label> (required), <motif> (required), <page> (required), <description> (optional)

FORBIDDEN ELEMENTS (every one of these causes a deployment error)

<sobjectName>, <name>, <fullName>, <apiVersion>, <isHidden>, <tabVisibility>, <type>, <mobileReady>, <urlFrameHeight>, <urlType>, <urlRedirect>, <encodingKey>, <height>, <auraComponent>

Also forbidden:

  • <label> on object tabs (object tabs inherit their label from the custom object)
  • <page> on web tabs (only for Visualforce tabs)
  • Empty elements like <page></page> or <description></description>
  • Any element not in the allowlist table above

Tab Types

Object Tabs

  • Purpose: Navigate to custom or standard objects
  • File name determines the object: {ObjectApiName}.tab-meta.xml (e.g., Space_Station__c.tab-meta.xml)
  • Required elements: <customObject>true</customObject> and <motif>
  • Correct example (for a Space_Station__c.tab-meta.xml):
<?xml version="1.0" encoding="UTF-8"?>
<CustomTab xmlns="http://soap.sforce.com/2006/04/metadata">
    <customObject>true</customObject>
    <motif>Custom39: Telescope</motif>
</CustomTab>
  • Correct example (for a Supply__c.tab-meta.xml — note different motif):
<?xml version="1.0" encoding="UTF-8"?>
<CustomTab xmlns="http://soap.sforce.com/2006/04/metadata">
    <customObject>true</customObject>
    <motif>Custom98: Truck</motif>
</CustomTab>
  • ❌ WRONG — do NOT add <sobjectName>, <name>, <fullName>, or <label>:
<CustomTab xmlns="http://soap.sforce.com/2006/04/metadata">
    <sobjectName>Space_Station__c</sobjectName>  <!-- DEPLOYMENT ERROR -->
    <label>Space Station</label>                  <!-- DEPLOYMENT ERROR on object tabs -->
    <customObject>true</customObject>
    <motif>Custom57: Desert</motif>
</CustomTab>

Web Tabs

  • Purpose: Link to external websites or web applications
  • File name: Use a descriptive name: {TabName}.tab-meta.xml (e.g., Knowledge_Base.tab-meta.xml)
  • COPY THIS EXACT TEMPLATE — only replace the placeholder values. Do NOT add, remove, or rename any XML elements:
<?xml version="1.0" encoding="UTF-8"?>
<CustomTab xmlns="http://soap.sforce.com/2006/04/metadata">
    <customObject>false</customObject>
    <description>REPLACE_WITH_DESCRIPTION</description>
    <frameHeight>600</frameHeight>
    <label>REPLACE_WITH_LABEL</label>
    <motif>REPLACE_WITH_MOTIF</motif>
    <url>REPLACE_WITH_URL</url>
    <urlEncodingKey>UTF-8</urlEncodingKey>
</CustomTab>
  • These 7 elements above are the ONLY elements allowed in a web tab file. Do not add ANY other elements.
  • The <description> element is optional — you may remove it if not needed, but do not add anything else.

Visualforce Tabs

  • Purpose: Access custom Visualforce pages
  • File name: {TabName}.tab-meta.xml (e.g., Custom_Page_Tab.tab-meta.xml)
  • Required elements: <customObject>false</customObject>, <label>, <motif>, <page>
  • Correct example:
<?xml version="1.0" encoding="UTF-8"?>
<CustomTab xmlns="http://soap.sforce.com/2006/04/metadata">
    <customObject>false</customObject>
    <label>Custom Page</label>
    <motif>Custom46: Computer</motif>
    <page>CustomPage</page>
</CustomTab>

Tab Configuration

Tab Style

  • Default: Use standard tab styling
  • Custom: Can specify custom tab styles if needed

Tab Visibility

  • Default: Visible to all users with access
  • Custom: Can be configured for specific user profiles

Supported Applications

  • Standard Apps: Available in standard Salesforce applications
  • Custom Apps: Can be included in custom applications
  • Community Apps: Available in community applications

Integration Points

  • Object Relationships: Links to related object records
  • Web Content: External website integration
  • Visualforce Pages: Custom page functionality
  • Lightning Components: Modern component integration

Best Practices

  • Use clear, descriptive tab labels
  • Choose appropriate tab types for functionality
  • Select a unique, contextually relevant motif for each tab — do not default every tab to the same icon
  • Consider user experience and navigation flow
  • Test tab functionality across different applications
  • Ensure proper permissions and visibility settings
  • Follow consistent naming conventions
  • Object tab files MUST only contain <customObject>true</customObject> and <motif> — nothing else
  • Web tab files MUST only contain: <customObject>false</customObject>, <label>, <motif>, <url>, <urlEncodingKey>, and optionally <description>, <frameHeight> — nothing else
  • Never include <isHidden>, <tabVisibility>, <type>, <mobileReady>, or empty elements
根据自然语言描述构建完整的Salesforce Lightning应用,协调对象、字段、页面等依赖元数据,按正确顺序生成可部署的应用程序。适用于多组件业务解决方案场景。
用户请求构建完整的Lightning应用或端到端解决方案 描述需要多个相互关联的Salesforce组件(如对象、页面、权限集)的业务场景
skills/platform-lightning-app-coordinate/SKILL.md
npx skills add forcedotcom/sf-skills --skill platform-lightning-app-coordinate -g -y
SKILL.md
Frontmatter
{
    "name": "platform-lightning-app-coordinate",
    "metadata": {
        "version": "1.0",
        "related-skills": "platform-custom-object-generate, platform-custom-field-generate, platform-custom-tab-generate, platform-flexipage-generate, platform-custom-application-generate, automation-flow-generate, platform-validation-rule-generate, platform-list-view-generate, platform-permission-set-generate"
    },
    "description": "Build complete Salesforce Lightning Experience applications from natural language descriptions. Use this skill when a user requests a \"complete app\", \"Lightning app\", \"business solution\", \"management system\", or describes a scenario requiring multiple interconnected Salesforce components (objects, fields, pages, tabs, security). Orchestrates all required metadata types in proper dependency order to produce a deployable application."
}

Generating Lightning App

Overview

Build a complete, deployable Salesforce Lightning Experience application from a natural language description by defining a Lightning Custom Application and orchestrating its dependent metadata types in correct dependency order. Invoke specialized metadata skills when available; generate metadata directly when no skill exists.

When to Use This Skill

Use when:

  • User requests a "Lightning app", or "end-to-end solution"
  • User says "build an app", "create an application", "build a [type] app" (project management, tracking, etc.)
  • The work produces a custom app (CustomApplication) plus supporting metadata, not a lone object, page, or tab in isolation

Examples that should trigger this skill:

  • "Build a project management lightning app with Tasks, Resources, and Supplies objects"
  • "Create a LEX app to track vehicles with Lightning pages and permission sets"
  • "I need a Space Station management system with multiple objects and relationships"
  • "Build an employee onboarding lightning app with custom Lightning Record Pages"

Do NOT use when:

  • Creating a single metadata component (use specific metadata skill instead)
  • Troubleshooting or debugging existing metadata
  • Building Salesforce Classic apps (not Lightning Experience)
  • User asks for just one object, or just one page, or just one permission set (without others)
  • User only needs to create or configure an app container (grouping existing tabs) without other metadata; use platform-custom-application-generate instead

Metadata Type Registry

This table shows which metadata types are commonly needed for Lightning Experience apps, their skill availability, and API context requirement.

Metadata Type Skill Name API Context Usage Rule
Custom Object platform-custom-object-generate salesforce-api-context MUST load skill AND call API context
Custom Field platform-custom-field-generate salesforce-api-context MUST load skill AND call API context
Custom Tab platform-custom-tab-generate salesforce-api-context MUST load skill AND call API context
FlexiPage platform-flexipage-generate salesforce-api-context MUST load skill AND call API context
Custom Application platform-custom-application-generate salesforce-api-context MUST load skill AND call API context
List View platform-list-view-generate salesforce-api-context MUST load skill AND call API context (if requested)
Validation Rule platform-validation-rule-generate salesforce-api-context MUST load skill AND call API context (if requested)
Flow automation-flow-generate metadata-experts pipeline MUST load skill AND run pipeline. Exempt from salesforce-api-context.
Permission Set platform-permission-set-generate salesforce-api-context MUST load skill AND call API context

Usage Rules

SKILL RULE: When a skill exists for a metadata type, you MUST load that skill. Do NOT generate metadata directly without loading the skill first.

API CONTEXT RULE: For every metadata type (except Flow), you MUST call salesforce-api-context tools before generating. Do NOT generate metadata without calling API context first. The skill provides structure and rules; API context confirms what is valid for the current API version. Both are essential.

FALLBACK RULE: When no skill exists for a metadata type you need, generate the metadata directly using your knowledge of Salesforce Metadata API and best practices. API context is still required.

RATIONALE: Skills contain validated patterns and constraints. API context provides version-specific accuracy. Together they prevent deployment failures.


Dependency Graph & Build Order

Phase 1: Data Model (Foundation)

Custom Objects (no dependencies)
    ↓
Custom Fields (depends on: Objects exist)
    ↓
Relationships (depends on: Both parent and child objects + fields exist)

Metadata types in this phase:

  1. platform-custom-object-generate - once, with all objects
  2. platform-custom-field-generate - once, with all fields (including Master-Detail, Lookup, Roll-up Summary)

Phase 2: Business Logic (Optional - only if requested)

Validation Rules (depends on: Fields exist)
    ↓
Flows (depends on: Objects, Fields exist)

Metadata types in this phase (only if user requested):

  1. platform-validation-rule-generate - once, if validation requirements mentioned
  2. automation-flow-generate - once, if automation/workflow requirements mentioned

Phase 3: User Interface

List Views (depends on: Objects, Fields exist)
    ↓
Custom Tabs (depends on: Objects exist)
    ↓
FlexiPages (depends on: Objects, Tabs exist)

Metadata types in this phase:

  1. platform-list-view-generate - once, for filtered record views (if requested)
  2. platform-custom-tab-generate - once, with all object tabs
  3. platform-flexipage-generate - once, with all record/home/app pages

Phase 4: Application Assembly

Custom Application (depends on: Tabs exist)

Metadata types in this phase:

  1. platform-custom-application-generate - once, to create the Lightning App container

Phase 5: Security & Access

Permission Sets (depends on: Objects, Fields, Tabs, App exist)

Metadata types in this phase:

  1. platform-permission-set-generate - once, with all permission sets and access to:
    • Objects (Read, Create, Edit, Delete)
    • Fields (Read, Edit)
    • Tabs (Visible)
    • Custom Application (Visible)

Execution Workflow

STEP 1: Requirements Analysis & Planning

Actions:

  1. Parse user's natural language request
  2. Extract business entities (become Custom Objects)
  3. Extract attributes/properties (become Custom Fields)
  4. Identify relationships (Master-Detail, Lookup)
  5. Detect validation requirements (become Validation Rules)
  6. Detect automation requirements (become Flows)
  7. Identify user personas (inform Permission Sets)

Output: Build Plan

Generate a structured plan listing:

Lightning App Build Plan: [App Name]

DATA MODEL:
- Custom Objects: [list with object names]
- Custom Fields: [list grouped by object]
- Relationships: [list M-D and Lookup relationships]

BUSINESS LOGIC (if applicable):
- Validation Rules: [list with object and rule name]
- Flows: [list with flow name and type]

USER INTERFACE:
- List Views (if requested): [list with object and view name]
- Custom Tabs: [list with object]
- FlexiPages: [list with page name and type]
- Custom Application: [app name]

SECURITY:
- Permission Sets: [list with purpose]

PER-TYPE EXECUTION (skill + API context for each):
- CustomObject: load platform-custom-object-generate + call salesforce-api-context
- CustomField: load platform-custom-field-generate + call salesforce-api-context
- ValidationRule: load platform-validation-rule-generate + call salesforce-api-context (if requested)
- Flow: load automation-flow-generate + run metadata-experts pipeline (if requested)
- ListView: load platform-list-view-generate + call salesforce-api-context (if requested)
- CustomTab: load platform-custom-tab-generate + call salesforce-api-context
- FlexiPage: load platform-flexipage-generate + call salesforce-api-context
- CustomApplication: load platform-custom-application-generate + call salesforce-api-context
- PermissionSet: load platform-permission-set-generate + call salesforce-api-context

STATUS LINES TO EMIT BEFORE FILE WRITES:
- `type=<Type> skill=complete mcp=complete|unavailable mcp_tools=<tool-list|none>`
- Flow exception: `type=Flow skill=complete pipeline=complete`

DEPENDENCY ORDER:
1. Phase 1: Data Model (Objects -> Fields)
2. Phase 2: Business Logic (Validation Rules -> Flows)
3. Phase 3: User Interface (List Views -> Tabs -> Pages)
4. Phase 4: App Assembly (Application)
5. Phase 5: Security (Permission Sets)

STEP 2: Per-Type Execution

Execute these four steps for each metadata type, one type at a time. Complete all four steps for the current type before moving to the next type. Do NOT skip any step.

Step What to do Why
① Load skill Search for and read the per-type SKILL.md Gives you the XML structure, required elements, naming rules, and validation constraints
② Call API context Call salesforce-api-context tools for this metadata type using one or more of: get_metadata_type_sections, get_metadata_type_context, get_metadata_type_fields, get_metadata_type_fields_properties, search_metadata_types Gives you the current valid values — allowed enum values, required vs. optional fields, child types for this API version. The skill provides structure; API context provides version-specific accuracy.
③ Record status Emit: type=<Type> skill=complete mcp=complete|unavailable mcp_tools=<tool-list|none> Confirms both steps were attempted before any files are written and records which API context tools were used
④ Generate files Generate all files for this type, then checkpoint Only after ①②③ are done. Verify, then move to the next type.

Do NOT combine ① and ② into a single action or skip ② after completing ①. They are separate steps that serve different purposes. After loading the skill you may feel ready to generate — stop and do ② first.

If salesforce-api-context is unavailable after a real attempt, record mcp=unavailable and generate using skill knowledge alone. Not attempting ② at all is a bug.


1. Custom Objects

  • ① Load skill: Read platform-custom-object-generate SKILL.md
  • ② API context: Call salesforce-api-context for CustomObject
  • ③ Status: type=CustomObject skill=complete mcp=complete|unavailable mcp_tools=<tool-list|none>
  • ④ Generate + Checkpoint: Generate all Custom Object files, then proceed to #2

2. Custom Fields

  • ① Load skill: Read platform-custom-field-generate SKILL.md
  • ② API context: Call salesforce-api-context for CustomField
  • ③ Status: type=CustomField skill=complete mcp=complete|unavailable mcp_tools=<tool-list|none>
  • ④ Generate + Checkpoint: Generate all Custom Field files, then proceed to #3

3. Validation Rules (only if requested)

  • ① Load skill: Read platform-validation-rule-generate SKILL.md
  • ② API context: Call salesforce-api-context for ValidationRule
  • ③ Status: type=ValidationRule skill=complete mcp=complete|unavailable mcp_tools=<tool-list|none>
  • ④ Generate + Checkpoint: Generate all Validation Rule files, then proceed to #4

4. Flows (only if requested)

  • ① Load skill: Read automation-flow-generate SKILL.md
  • ② Pipeline: Run metadata-experts/execute_metadata_action 3-step pipeline (exempt from salesforce-api-context)
  • ③ Status: type=Flow skill=complete pipeline=complete
  • ④ Generate + Checkpoint: Generate all Flow files via the pipeline, then proceed to #5

5. List Views (only if requested)

  • ① Load skill: Read platform-list-view-generate SKILL.md
  • ② API context: Call salesforce-api-context for ListView
  • ③ Status: type=ListView skill=complete mcp=complete|unavailable mcp_tools=<tool-list|none>
  • ④ Generate + Checkpoint: Generate all List View files, then proceed to #6

6. Custom Tabs

  • ① Load skill: Read platform-custom-tab-generate SKILL.md
  • ② API context: Call salesforce-api-context for CustomTab
  • ③ Status: type=CustomTab skill=complete mcp=complete|unavailable mcp_tools=<tool-list|none>
  • ④ Generate + Checkpoint: Generate all Custom Tab files, then proceed to #7

7. FlexiPages

  • ① Load skill: Read platform-flexipage-generate SKILL.md
  • ② API context: Call salesforce-api-context for FlexiPage
  • ③ Status: type=FlexiPage skill=complete mcp=complete|unavailable mcp_tools=<tool-list|none>
  • ④ Generate + Checkpoint: Generate all FlexiPage files, then proceed to #8

8. Custom Application

  • ① Load skill: Read platform-custom-application-generate SKILL.md
  • ② API context: Call salesforce-api-context for CustomApplication
  • ③ Status: type=CustomApplication skill=complete mcp=complete|unavailable mcp_tools=<tool-list|none>
  • ④ Generate + Checkpoint: Generate the Custom Application file, then proceed to #9

9. Permission Sets

  • ① Load skill: Read platform-permission-set-generate SKILL.md
  • ② API context: Call salesforce-api-context for PermissionSet
  • ③ Status: type=PermissionSet skill=complete mcp=complete|unavailable mcp_tools=<tool-list|none>
  • ④ Generate + Checkpoint: Generate all Permission Set files — all types complete

STEP 3: Final Artifact Assembly

After all phases complete, consolidate outputs into deployment-ready structure.


Output

The completed build produces:

  1. Salesforce DX Project Directory containing all generated metadata

    • Organized by standard SFDX structure: force-app/main/default/
  2. Metadata Files - One file per component, organized by type:

    force-app/main/default/
    ├── objects/              # Custom Objects (.object-meta.xml)
    ├── fields/               # Custom Fields (.field-meta.xml)
    ├── tabs/                 # Custom Tabs (.tab-meta.xml)
    ├── flexipages/           # Lightning Pages (.flexipage-meta.xml)
    ├── applications/         # Custom Applications (.app-meta.xml)
    ├── permissionsets/       # Permission Sets (.permissionset-meta.xml)
    ├── flows/                # Flows (.flow-meta.xml) - if applicable
    └── objects/.../validationRules/  # Validation Rules (.validationRule-meta.xml) - if applicable
    
  3. Deployment Manifest (package.xml)

    • Lists all components with proper API version
    • Organized by metadata type in dependency order
    • Ready for Salesforce CLI deployment or Metadata API deployment
  4. Build Summary Report - A markdown file listing:

    • Every component created
    • Component type and API name
    • File path location
    • Dependency relationships
    • Any warnings or recommendations

Example Summary Structure:

Lightning App Build Complete: Project Management App

METADATA GENERATED:
1 Custom Objects
   - Project__c -> force-app/main/default/objects/Project__c/Project__c.object-meta.xml
   - Task__c -> force-app/main/default/objects/Task__c/Task__c.object-meta.xml
   - Resource__c -> force-app/main/default/objects/Resource__c/Resource__c.object-meta.xml

2 Custom Fields
   - Project__c.Name -> force-app/main/default/objects/Project__c/fields/Name.field-meta.xml
   - Project__c.Status__c -> force-app/main/default/objects/Project__c/fields/Status__c.field-meta.xml
   [... etc ...]

3 Custom Tabs
   - Project__c -> force-app/main/default/tabs/Project__c.tab-meta.xml
   [... etc ...]

4 Lightning Record Pages
   - Project_Record_Page -> force-app/main/default/flexipages/Project_Record_Page.flexipage-meta.xml
   [... etc ...]

5 Custom Application
   - Project_Management -> force-app/main/default/applications/Project_Management.app-meta.xml

6 Permission Sets
   - Project_Manager -> force-app/main/default/permissionsets/Project_Manager.permissionset-meta.xml
   - Project_User -> force-app/main/default/permissionsets/Project_User.permissionset-meta.xml

WARNINGS: None

Validation

Before presenting the completed build to the user, verify cross-component integrity:

  • Object-Tab Coverage: Every Custom Object has at least one Custom Tab
  • Relationship Integrity: Every Custom Object referenced in a relationship (parent or child) exists in the build
  • Field References in Pages: Every field referenced in a FlexiPage exists on the corresponding object
  • Tab References in App: Every tab referenced in the Custom Application was successfully created
  • Permission Set Completeness: Permission Sets grant access to all generated objects, fields, tabs, and the application
  • No Orphaned Components: No tabs without objects, no pages without corresponding tabs, no app without tabs
  • Deployment Manifest Completeness: package.xml includes all generated components in proper dependency order

Validation Failure Handling (Category 2):

  • If validation fails, include failed checks in the Build Summary Report under a VALIDATION WARNINGS section
  • These are post-generation issues — do NOT block delivery of the build, but clearly communicate what needs manual review or correction
  • Provide specific remediation steps for each failed validation check

Note: Individual component validations (reserved words, name lengths, field types, etc.) are handled by specialized metadata skills and do not need to be re-validated here.


Error Handling

Category 1: Stop and Ask User

Stop execution and ask for clarification if:

  • User request is too vague to extract any objects or fields
  • Conflicting requirements detected (e.g., "make it private" + "everyone should see it")
  • Invalid Salesforce naming detected (reserved words like Order, Group)

Category 2: Post-Generation Warnings (Log Warning, Continue)

Log warning and continue if:

  • Cross-component validation check fails (e.g., field referenced in FlexiPage doesn't exist on object)
  • Optional component generation fails (e.g., List View generation has minor issues)
  • Validation Rule or Flow has minor output issues

Warning Pattern:

Warning: [Component Type] generation encountered issue
    Component: [Name]
    Issue: [Description]
    Impact: [What won't work]
    Recommendation: [How to fix manually]
    Continuing with remaining components...

Best Practices

1. Always Follow Dependency Order

Never invoke skills out of sequence. Fields need objects, pages need tabs, apps need tabs.

2. Use Skills When Available

Don't reinvent the wheel. Specialized skills have field-specific validation that prevents deployment errors.

3. Generate Thoughtful Defaults

When user doesn't specify details:

  • Use Text name fields for human entities
  • Use AutoNumber for transactions
  • Enable Search and Reports for user-facing objects
  • Set sharingModel based on relationships

4. Validate Before Building

Check for:

  • Reserved words in API names
  • Relationship limits (max 2 M-D per object)
  • Name length limits
  • Duplicate names
用于生成、创建和验证Salesforce List View元数据。支持配置过滤条件、列显示及可见性策略,处理ListView XML文件校验与部署排错,确保字段命名规范。
用户需要创建或生成Salesforce列表视图 涉及List View元数据的XML文件验证或故障排除 询问关于列表视图的过滤条件、列配置或可见性设置
skills/platform-list-view-generate/SKILL.md
npx skills add forcedotcom/sf-skills --skill platform-list-view-generate -g -y
SKILL.md
Frontmatter
{
    "name": "platform-list-view-generate",
    "metadata": {
        "version": "1.0"
    },
    "description": "Use this skill when users need to create, generate, or validate Salesforce List View metadata. Trigger when users mention list views, filtered record lists, creating views, setting up record columns, filtering records by criteria, or ask about list view visibility. Also use when users say things like \"I need a view that shows...\", \"filter records by...\", \"create a list view for...\", or when they're working with ListView XML files and need validation or troubleshooting."
}

When to Use This Skill

Use this skill when you need to:

  • Create list views for objects
  • Generate filtered, column-based record listings
  • Configure list view visibility and sharing
  • Troubleshoot deployment errors related to List Views

Specification

Salesforce List View Metadata Knowledge

Overview

Salesforce List Views define filtered, column-based record listings on an object's tab.

Purpose

  • Provide curated, role- or task-specific subsets of records
  • Standardize commonly used filters and visible fields across teams

Configuration

Unless specifically requested to be generated inline, List Views are stored at:

  • force-app/main/default/objects/<ObjectName>/listViews/<fullName>.listView-meta.xml Only if the user requests are they to be included in the object's metadata file:
  • fore-app/main/default/objects/<ObjectName>/<ObjectName>.object-meta.xml

Key elements:

  • label: Human-friendly name shown in UI (must be under 40 characters in length)
  • fullName (fullName): API identifier used in metadata and file name
  • filterScope: Everything | Mine | Queue
  • filters: field/operation/value triples
  • booleanFilterLogic: Combine multiple filters logically with AND/OR (e.g., "1 AND (2 OR 3)")
  • columns: Ordered list of field API names to display

References:

  • listViews appear on the entity's tab
  • listViews can be referenced by flexipages using the "filterListCard" component

Critical Decision: Visibility Strategy

Choose how broadly the view should appear in the org.

Choose "Visible to all users" when:

  • The view is useful across profiles/roles
  • It's a governed, shared artifact to be managed via source control
  • Data contained is appropriate for broad visibility

Choose "Owner-only/Restricted" when:

  • It is experimental or niche during iteration
  • It is specifically requested to be limited to Users, Groups or Roles
  • There are governance/security reviews pending

When in doubt: Default to "Visible to all users".

Critical Decision: Columns Density

Choose minimal, high-signal columns when:

  • Users need at-a-glance scanning
  • Mobile/responsive performance matters

Choose richer column sets when:

  • Desktop heavy workflows need more context without opening records
  • It serves as a work queue and extra fields reduce clicks

When in doubt: Start with 4–6 columns that directly support the primary task.

Critical Rules (Read First)

Rule 1: Custom Field API Names

For custom fields, use exact API names (e.g., Status__c), not labels.

Wrong:

  • Status (label)

Right:

  • Status__c (API name)

Rule 2: Standard Field Names

For standard fields on Custom Objects, use already defined names:

Wrong:

  • Name (API Name)

Right:

  • NAME

The standard fields on Custom Objects are:

  • NAME
  • RECORDTYPE
  • OWNER.ALIAS
  • OWNER.FIRST_NAME
  • OWNER.LAST_NAME
  • CREATEDBY_USER.ALIAS
  • CREATEDBY_USER
  • CREATED_DATE
  • UPDATEDBY_USER.ALIAS
  • UPDATEDBY_USER
  • LAST_UPDATE
  • LAST_ACTIVITY

Rule 3: Operations Must Match Field Types

Picklists require equals/notEqual; date fields require date operators; boolean values are 0 and 1; do not mix text-only operators with non-text fields.

Wrong:

  • operation="contains" on a picklist
  • value=True on a boolean

Right:

  • operation="equals" with a valid picklist value
  • value=1 on a boolean

Rule 4: Name and Path Alignment

File name, fullName (also sometimes referred to as DeveloperName), and uniqueness must align.

Wrong:

  • File: My_List.listView-meta.xml
  • fullName: MyList

Right:

  • File: MyList.listView-meta.xml
  • fullName: MyList

Rule 5: Folder Placement

Place files under the object's listViews directory or deployments will fail to resolve components. Only if a user requests it, may the listView may be included inline in force-app/main/default/objects/<ObjectName>/<ObjectName>.object-meta.xml

Path:

  • force-app/main/default/objects/<ObjectName>/listViews/<fullName>.listView-meta.xml

Generation Workflow

Step 1: Get Metadata Information

  • Identify the target object API name (e.g., Object__c).
  • Gather business requirements: purpose, audience, fields, filters.
  • Validate values and operator compatibility with field types.

Step 2: Examine Existing Examples

  • Repo: force-app/main/default/objects/<Object>/listViews/ (unless otherwise required by end user)
  • Org: retrieve existing list views for proven patterns (filters, logic, columns).
  • Note what passed review/deployment and delivered expected UX.

Step 3: Create Specification

Document before implementation:

  • Name: fullName and Label
  • Audience: Visibility scope ("all users" vs. shared)
  • Filter scope: Everything | Mine | Queue
  • Filter items: filter, operator, value; plus booleanFilterLogic if multiple
  • Columns: Ordered list of field API names
  • Acceptance criteria: Which records appear, paging behavior, key scenarios

Step 4: Author Metadata File

Use a Lightning-compatible template and ensure valid XML:

<?xml version="1.0" encoding="UTF-8"?>
<ListView xmlns="http://soap.sforce.com/2006/04/metadata">
    <fullName>OpenMine</fullName>
    <label>Open - My Records</label>
    <filterScope>Mine</filterScope>
    <columns>NAME</columns>
    <columns>Status__c</columns>
    <columns>OWNER.ALIAS</columns>
    <columns>LAST_UPDATE</columns>
    <filters>
        <field>Status__c</field>
        <operation>equals</operation>
        <value>Open</value>
    </filters>
    <sharedTo>
        <role>CEO</role>
        <roleAndSubordinatesInternal>COO</roleAndSubordinatesInternal>
    </sharedTo>
</ListView>

Notes:

  • For "My" views, use filterScope="Mine".
  • Keep columns tight and purposeful.
  • If intended for all users, omit the "sharedTo" section.

Step 5: Validate Locally

  • Well-formed XML; correct namespace
  • Field names exist on the object; operators and values match field types
  • Path and fullName alignment
  • If multiple filters: set booleanFilterLogic correctly (e.g., "1 AND (2 OR 3)")

Step 6: Deploy and Verify in Org

  • Deploy the component path or the whole object.
  • In the UI, open the object tab and:
    • Confirm records match filters
    • Confirm columns render correctly
    • Confirm visibility matches audience

Common Deployment Errors

Error Cause Fix
"Invalid field Status" Used label instead of API name, or used API Name instead of defined name for Standard Field Use Status__c (or correct API name), or NAME instead of Name (for Standard Fields)
"Invalid filter operator" Operator not valid for field type Choose operation compatible with field type (e.g., equals for picklist)
"Component not found at path" Wrong folder or file name Place in objects/<Object>/listViews and align file name with fullName
"Malformed booleanFilterLogic" Syntax or index mismatch Use "1 AND 2" style, ensure filters index order matches

Verification Checklist

  • All required fields populated (fullName, label, filterScope, columns)
  • Property values are XML-encoded where needed
  • Custom Field references use API names (e.g., Status__c)
  • Standard Field references use defined names (e.g., NAME)
  • Operations match field types; picklist values are valid
  • booleanFilterLogic (if used) matches filters ordering and count
  • File path and fullName/developerName are aligned
  • No deprecated or Classic-only properties included
  • Deployed successfully and visible as intended
  • Records, columns, and filtering behave as specified
基于sf CLI v2的Salesforce DevOps自动化技能,负责元数据部署编排、预检验证、CI/CD流程建议及故障排查。涵盖干跑测试、目标部署、Scratch Org管理及发布顺序优化,排除代码生成与数据查询任务。
执行元数据部署或检索操作 创建或管理Scratch Org及沙盒环境 配置CI/CD流水线 排查部署错误及依赖问题
skills/platform-metadata-deploy/SKILL.md
npx skills add forcedotcom/sf-skills --skill platform-metadata-deploy -g -y
SKILL.md
Frontmatter
{
    "name": "platform-metadata-deploy",
    "metadata": {
        "version": "1.1"
    },
    "description": "Salesforce DevOps automation using sf CLI v2. TRIGGER when: user deploys metadata, creates\/manages scratch orgs or sandboxes, sets up CI\/CD pipelines, or troubleshoots deployment errors with sf project deploy. DO NOT TRIGGER when: writing Apex code (use platform-apex-generate), building LWC components (use experience-lwc-generate), creating metadata definitions (use platform-custom-object-generate or platform-custom-field-generate), or querying org data (use platform-data-manage)."
}

platform-metadata-deploy: Comprehensive Salesforce DevOps Automation

Use this skill when the user needs deployment orchestration: dry-run validation, targeted or manifest-based deploys, CI/CD workflow advice, scratch-org management, failure triage, or safe rollout sequencing for Salesforce metadata.

When This Skill Owns the Task

Use platform-metadata-deploy when the work involves:

  • sf project deploy start, quick, report, or retrieval workflows
  • release sequencing across objects, permission sets, Apex, and Flows
  • CI/CD gates, test-level selection, or deployment reports
  • troubleshooting deployment failures and dependency ordering

Delegate elsewhere when the user is:


Critical Operating Rules

  • Use sf CLI v2 only.
  • On non-source-tracking orgs, deploy/retrieve commands require an explicit scope such as --source-dir, --metadata, or --manifest.
  • Prefer --dry-run first before real deploys.
  • For Flows, deploy safely and activate only after validation.
  • Keep test-data creation guidance delegated to platform-data-manage after metadata is validated or deployed.

Default deployment order

Phase Metadata
1 Custom objects / fields
2 Permission sets
3 Apex
4 Flows as Draft
5 Flow activation / post-verify

This ordering prevents many dependency and FLS failures.


Required Context to Gather First

Ask for or infer:

  • target org alias and environment type
  • deployment scope: source-dir, metadata list, or manifest
  • whether this is validate-only, deploy, quick deploy, retrieve, or CI/CD guidance
  • required test level and rollback expectations
  • whether special metadata types are involved (Flow, permission sets, agents, packages)

Preflight checks:

sf --version
sf org list
sf org display --target-org <alias> --json
test -f sfdx-project.json

Recommended Workflow

1. Preflight

Confirm auth, repo shape, package directories, and target scope.

2. Validate first

sf project deploy start --dry-run --source-dir force-app --target-org <alias> --wait 30 --json

Use manifest- or metadata-scoped validation when the change set is targeted.

3. If validation succeeds, offer the next safe workflow

After a successful validation, guide the user to the correct next action:

  1. deploy now
  2. assign permission sets
  3. create test data via platform-data-manage
  4. run tests / smoke checks
  5. orchestrate multiple post-deploy steps in order

4. Deploy the smallest correct scope

# source-dir deploy
sf project deploy start --source-dir force-app --target-org <alias> --wait 30 --json

# manifest deploy
sf project deploy start --manifest manifest/package.xml --target-org <alias> --test-level RunLocalTests --wait 30 --json

# manifest deploy with Spring '26 relevant-test selection
sf project deploy start --manifest manifest/package.xml --target-org <alias> --test-level RunRelevantTests --wait 30 --json

# quick deploy after successful validation
sf project deploy quick --job-id <validation-job-id> --target-org <alias> --json

5. Verify

sf project deploy report --job-id <job-id> --target-org <alias> --json

Then verify tests, Flow state, permission assignments, and smoke-test behavior.

6. Report clearly

Summarize what deployed, what failed, what was skipped, and what the next safe action is.

Output template: references/deployment-report-template.md


High-Signal Failure Patterns

Error / symptom Likely cause Default fix direction
FIELD_CUSTOM_VALIDATION_EXCEPTION validation rule or bad test data adjust data or rule timing
INVALID_CROSS_REFERENCE_KEY missing dependency include referenced metadata first
CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY trigger / Flow / validation side effect inspect automation stack and failing logic
tests fail during deploy broken code or fragile tests run targeted tests, fix root cause, revalidate
field/object not found in permset wrong order deploy objects/fields before permission sets
Flow invalid / version conflict dependency or activation problem deploy as Draft, verify, then activate

Full workflows: references/orchestration.md, references/trigger-deployment-safety.md


CI/CD Guidance

Default pipeline shape:

  1. authenticate
  2. validate repo / org state
  3. static analysis
  4. dry-run deploy
  5. tests + coverage gates
  6. deploy
  7. verify + notify
  • When org policy and release risk allow it, consider --test-level RunRelevantTests for Apex-heavy deployments.
  • Pair this with modern Apex test annotations such as @IsTest(testFor=...) and @IsTest(isCritical=true) — see platform-apex-generate for authoring guidance.

Static analysis now uses Code Analyzer v5 (sf code-analyzer), not retired sf scanner.

Deep reference: references/deployment-workflows.md


Agentforce Deployment Note

Use this skill to orchestrate deployment/publish sequencing around agents, but use the agent-specific skill for authoring decisions:

For full agent DevOps details, including Agent: pseudo metadata, publish/activate, and sync-between-orgs, see:


Cross-Skill Integration

Need Delegate to Reason
custom object creation platform-custom-object-generate define objects before deploy
custom field creation platform-custom-field-generate define fields before deploy
Apex authoring / fixes platform-apex-generate code authoring and repair
Flow creation / repair automation-flow-generate Flow authoring and activation guidance
test data or seed records platform-data-manage describe-first data setup and cleanup
Agent authoring and publish readiness agentforce-generate agent-specific correctness

Reference Map

Start here

Specialized deployment safety

Asset templates


Score Guide

Score Meaning
90+ strong deployment plan and execution guidance
75–89 good deploy guidance with minor review items
60–74 partial coverage of deployment risk
< 60 insufficient confidence; tighten plan before rollout

Completion Format

Deployment goal: <validate / deploy / retrieve / pipeline>
Target org: <alias>
Scope: <source-dir / metadata / manifest>
Result: <passed / failed / partial>
Key findings: <errors, ordering, tests, skipped items>
Next step: <safe follow-up action>
生成可部署的Salesforce权限集XML元数据,支持配置对象CRUD、字段级安全、用户系统权限及应用标签可见性,确保符合命名规范与平台限制。
创建或编辑PermissionSet XML 配置对象访问权限 设置字段级安全性(FLS) 分配用户系统权限 管理应用和标签可见性
skills/platform-permission-set-generate/SKILL.md
npx skills add forcedotcom/sf-skills --skill platform-permission-set-generate -g -y
SKILL.md
Frontmatter
{
    "name": "platform-permission-set-generate",
    "metadata": {
        "author": "sf-skills",
        "version": "1.0"
    },
    "description": "Generates correct, deployable Salesforce permission set metadata (PermissionSet XML) with object, field, user, and app permissions. Use this skill when creating or editing permission set metadata, object permissions, field-level security (FLS), tab visibility, or deploying permission sets.",
    "compatibility": "Salesforce Metadata API v60.0+"
}

When to Use This Skill

Use when generating or editing permission set metadata, or when granting object, field, user, and app permissions.

Step 1: Define Core Properties

Start by defining the required permission set properties:

<PermissionSet xmlns="http://soap.sforce.com/2006/04/metadata">
    <fullName>YourPermissionSetName</fullName>
    <label>Display Name for Administrators</label>
    <description>Clear description of purpose and intended audience</description>
</PermissionSet>

Naming conventions:

  • Use descriptive API names (e.g., Sales_Manager_Access)

Step 2: Configure Object Permissions

Add CRUD permissions for standard and custom objects:

<objectPermissions>
    <allowCreate>true</allowCreate>
    <allowRead>true</allowRead>
    <allowEdit>true</allowEdit>
    <allowDelete>false</allowDelete>
    <modifyAllRecords>false</modifyAllRecords>
    <viewAllRecords>false</viewAllRecords>
    <viewAllFields>false</viewAllFields>
    <object>Account</object>
</objectPermissions>

Step 3: Set Field-Level Security

Define field permissions for sensitive or custom fields:

<fieldPermissions>
    <editable>true</editable>
    <readable>true</readable>
    <field>Account.SSN__c</field>
</fieldPermissions>

Important:

  • Required fields must NEVER appear in list of field permissions. Granting field-level security on required fields is not allowed by the platform and will cause deployment failure.
  • Before adding any field, confirm from the object metadata that the field exists and is not required
  • A field is required when its metadata contains <required>true</required>:
  • Formula fields cannot be editable
  • Master-detail fields are required fields on the child (detail) object
<fields>
    <fullName>FieldName__c</fullName>
    <required>true</required>
</fields>
  • Use format ObjectName.FieldName for field references
  • Set both readable and editable to true when the user needs edit access; editable implies readable
  • If all fields should be visible, can alternatively enable the "viewAllFields" object permission

Step 4: Grant User Permissions

Add system-level permissions for features and capabilities:

<userPermissions>
    <enabled>true</enabled>
    <name>ApiEnabled</name>
</userPermissions>
<userPermissions>
    <enabled>true</enabled>
    <name>RunReports</name>
</userPermissions>

Common permissions:

  • ApiEnabled: API access
  • ViewSetup: View Setup menu
  • ManageUsers: User management
  • RunReports: Report execution

Security review required for:

  • ViewAllData: Read all records
  • ModifyAllData: Edit all records
  • ManageUsers: User administration

Step 5: Configure App and Tab Visibility

Make applications and tabs visible to users:

<applicationVisibilities>
    <application>Sales_Console</application>
    <visible>true</visible>
</applicationVisibilities>
<tabSettings>
    <tab>CustomTab__c</tab>
    <visibility>Visible</visibility>
</tabSettings>

Application visibility options:

  • can be true or false

Tab visibility options:

  • Visible: The tab is available on the All Tabs page and appears in the visible tabs for its associated app. Can be customized.
  • Available: The tab is available on the All Tabs page. Individual users can customize their display to make the tab visible in any app
  • None: Not visible

CRITICAL - Tab Naming:

  • Custom object tabs: MUST include the __c suffix (e.g., MyCustomObject__c)
  • Standard object tabs: Use the object name with "standard-" prefix (e.g., standard-Account, standard-Contact)
  • The tab name matches the object's API name exactly

Step 6: Add Apex and Visualforce Access (Optional)

Grant access to custom code:

<classAccesses>
    <apexClass>CustomController</apexClass>
    <enabled>true</enabled>
</classAccesses>
<pageAccesses>
    <apexPage>CustomPage</apexPage>
    <enabled>true</enabled>
</pageAccesses>

Step 7: Set License and Record Type Settings (Optional)

Specify license requirements and record type visibility:

<license>Salesforce</license>
<hasActivationRequired>false</hasActivationRequired>
<recordTypeVisibilities>
    <recordType>Account.Business</recordType>
    <visible>true</visible>
    <default>true</default>
</recordTypeVisibilities>

Step 8: Set Agent Access (Optional)

Enable access to Agentforce Employee Agents for users assigned to this permission set:

Sales_Assistant_Agent true

Field requirements:

  • agentName (Required): The developer name of the employee agent
  • enabled (Required): Set to true to grant access, false to deny

Important:

  • Agent names must match existing Agentforce Employee Agent developer names

Validation Checklist

Before deploying, verify:

  • fullName, label, description set
  • Permissions follow least privilege
  • No required fields in <fieldPermissions>
  • No duplicate permissions
  • No lengthy comments

What Causes Deployment Failure

  • Field permissions on required fields: Any required field in <fieldPermissions> fails deployment. Required fields cannot have FLS; omit them entirely. Always confirm from object/field metadata that a field exists and is not required—never assume.
  • Incorrect API names: Using the wrong name or missing suffixes (e.g. missing __c for custom objects, fields, tabs) cause failure.

Deployment

Deploy using Salesforce CLI

用于在Salesforce中创建、修改和验证数据验证规则。支持生成元数据、编写公式及错误消息,处理字段类型匹配、函数使用规范(如TEXT、ISPICKVAL)及XML CDATA格式,确保数据质量和业务逻辑正确执行。
用户需要创建或修改Salesforce验证规则 涉及字段验证、数据质量规则或公式验证 遇到验证错误或需要更新验证逻辑 要求强制实施数据层业务规则
skills/platform-validation-rule-generate/SKILL.md
npx skills add forcedotcom/sf-skills --skill platform-validation-rule-generate -g -y
SKILL.md
Frontmatter
{
    "name": "platform-validation-rule-generate",
    "metadata": {
        "version": "1.0"
    },
    "description": "Use this skill when users need to create, modify, or validate Salesforce Validation Rules. Trigger when users mention validation rules, field validation, data quality rules, formula validation, error messages, or validation logic. Also use when users encounter validation errors, need to update formulas, or want to enforce business rules at the data layer. Always use this skill for any validation rule work."
}

When to Use This Skill

Use this skill when you need to:

  • Create validation rules to enforce data quality
  • Prevent invalid records from being saved
  • Generate validation rule metadata with formulas
  • Add business logic validation to objects
  • Troubleshoot deployment errors related to validation rules

Specification

ValidationRule Metadata Specification

Overview

Validation Rules are declarative metadata components used to enforce data quality and business logic in Salesforce. They evaluate a formula expression when a record is saved and prevent the save operation if the expression returns TRUE.

Purpose

-Enforce business rules at the data layer -Prevent invalid or incomplete records from being saved -Display meaningful error messages to guide users

Required Properties

Core Validation Rule Properties

  • fullName

    • The unique API name of the validation rule
    • Must start with a letter
      • Can contain letters, numbers, and underscores
    • Cannot end with an underscore
    • Cannot contain consecutive underscores
    • Cannot exceed 40 character.
  • active -Indicates whether the validation rule is enabled true → Rule is enforced false → Rule is inactive

  • errorConditionFormula

  • The logical formula that evaluates record data

  • Must return TRUE or FALSE

  • If TRUE, the validation rule triggers an error

  • errorMessage

    • The message displayed to the user when validation fails
  • Maximum length: 255 characters

Specific Function Guidelines

  • TEXT - TEXT() function MUST NOT be used with Text fields, to fix this you can just remove the TEXT() function.
  • CASE - In salesforce CASE() function, last parameter is the default value. Admins often miss to provide this and number of parameters to CASE() function are always even.
  • VALUE - VALUE() function should only be used with Text fields. If a number is being used as a parameter to the VALUE() function, remove the VALUE() function.
  • DAY - DAY() function should only be used with Date fields. If a Datetime field is being used as a parameter to the DAY() function, convert it into a Date first.
  • MONTH - MONTH() function should only be used with Date fields. If a Datetime field is being used as a parameter to the MONTH() function, convert it into a Date first.
  • DATEVALUE - DATEVALUE() function should only be used with DateTime fields. If a Date is being used as a parameter to the DATEVALUE() function, remove the DATEVALUE() function.
  • ISPICKVAL - If checking equality of a picklist type field, the function ISPICKVAL() MUST be used.
  • ISCHANGE - Use ISCHANGE() function to check the value of a record has changed.

Critical Rules

  1. Formula XML Handling(MOST COMMON ERROR)

    • ANY errorConditionFormula containing XML tags MUST be inside a CDATA section in the metadata XML.
  2. Interpretation of "Update" Instructions. When receiving instructions to modify a formula, distinguish between a replacement and an addition:

    • "Update the formula to [Action]": Completely replace the existing formula logic with the new requirement.
    • "Update the formula to also [Action]": Keep the existing logic and append the new requirement (usually by wrapping the logic in an AND() or OR() function).
  3. File Format Requirement

    • Validation rule files MUST always use the .validationRule-meta.xml extension.
用于生成 Agentforce 代理的设计时架构快照。通过读取 BotDefinition、Planner、Topics 等元数据,输出人类可读的文档和 Mermaid 调用图。适用于描述、图表化、清单整理或版本对比特定代理的架构结构,不涉及运行时会话追踪。
用户请求描述或图表化特定 Agentforce 代理的架构、动作树、主题结构或工具清单 用户请求对特定代理的架构进行库存盘点、审计或不同版本(如 v3 vs v5)之间的差异对比
skills/agentforce-architecture-analyze/SKILL.md
npx skills add forcedotcom/sf-skills --skill agentforce-architecture-analyze -g -y
SKILL.md
Frontmatter
{
    "name": "agentforce-architecture-analyze",
    "metadata": {
        "version": "1.0"
    },
    "description": "Declared architecture snapshot for one Agentforce agent: planner, topics, actions, flows, Apex, prompt templates, and NGA plugins. Renders a human-readable architecture document and Mermaid invocation graph from design-time metadata (not runtime audit rows). TRIGGER when user asks to describe, diagram, inventory, audit, document, or diff (e.g. v3 vs v5) the architecture \/ action tree \/ topic structure \/ tool inventory of a specific agent by agent API name in a specific org. DO NOT TRIGGER for runtime session traces, conversation transcripts, generation timings, or gateway audit chains — this skill reads design-time metadata only (use agentforce-d360-analyze for session traces)."
}

agentforce-architecture-analyze — declared architecture snapshot

Design-time metadata tree for one Agentforce agent: planner → topics → actions → flows → Apex → prompts → NGA plugins. Reads declared metadata only — BotDefinition, GenAiPlanner*, GenAiPlugin*, GenAiFunction*, Flow, ApexClass, GenAiPromptTemplate. Does not read runtime audit rows.

Runtime budget: 30–45s typical, ≤60s hard cap on reference fixtures. Sequential baseline would be 90–220s; parallel Tooling SOQL fan-out delivers a 3–5× speedup. Large bots with many flows scale approximately linearly — each flow metadata retrieve is one round-trip.

Runs inline — no subagent. Every phase is deterministic file processing.

If the user hasn't given enough to proceed

When invoked with no agent_api_name AND no org alias, print the following block verbatim — do not paraphrase, do not pre-run any script. Trigger condition: $ARGUMENTS is empty OR names no agent (no --agent flag and no known agent API name in the prose) OR names no org (no --org flag and no known alias).

Which agent should I document, and in which org?

I need:

  • Agent API name — the DeveloperName of the BotDefinition (e.g. MyAgent, MySalesAgent). Not the label.
  • Org alias — for sf CLI auth (the alias you configured with sf org login)

Optional:

  • Version — an agent_version_api_name like v5. If omitted, I'll resolve the active BotVersion.
  • --force — ignore cached tree; re-fetch everything.
  • --reprobe — re-run the 7-day channel-probe cache (only needed after a Salesforce release).

I'll run the metadata pipeline inline. Artifacts land under ~/.vibe/data/agentforce-architecture-analyze/<org_id15>/<agent_api_name>__<agent_version>/ (overridable with --data-dir).

Pipeline invocation

When the user has supplied --org <alias> + --agent <api_name> (plus any optional flags), run this block. One python3 invocation drives the full pipeline. main.py writes .emit_ctx.json; emit_result.py reads it and prints the final === RESULT === block last to stdout.

set -euo pipefail

# zsh arrays are 1-indexed by default; bash arrays are 0-indexed.
# This block uses 0-indexed semantics throughout (_args[$i] starting at i=0),
# so under zsh + `set -u` the very first read of `_args[0]` would trip
# `parameter not set`. KSH_ARRAYS makes zsh treat arrays as 0-indexed,
# matching the bash shebang's expectation. No-op under bash.
[ -n "${ZSH_VERSION:-}" ] && setopt KSH_ARRAYS

SKILL_ROOT="${SKILL_ROOT:-${PLUGIN_ROOT:-$HOME/.vibe/skills}/agentforce-architecture-analyze}"

# Argument parser. Accepts both `--org foo` and `--org=foo`.
# `$ARGUMENTS` is the raw user input Claude Code substitutes.
ARG_ORG=""
ARG_AGENT=""
ARG_VERSION=""
ARG_FORCE=""
ARG_REPROBE=""
ARG_PARALLELISM=""
ARG_MAX_MERMAID=""

# shellcheck disable=SC2206
_args=($ARGUMENTS)
i=0
while [ $i -lt ${#_args[@]} ]; do
 tok="${_args[$i]}"
 case "$tok" in
 --org=*) ARG_ORG="${tok#--org=}" ;;
 --org) i=$((i+1)); ARG_ORG="${_args[$i]:-}" ;;
 --agent=*) ARG_AGENT="${tok#--agent=}" ;;
 --agent) i=$((i+1)); ARG_AGENT="${_args[$i]:-}" ;;
 --version=*) ARG_VERSION="${tok#--version=}" ;;
 --version) i=$((i+1)); ARG_VERSION="${_args[$i]:-}" ;;
 --parallelism=*) ARG_PARALLELISM="${tok#--parallelism=}" ;;
 --parallelism) i=$((i+1)); ARG_PARALLELISM="${_args[$i]:-}" ;;
 --max-mermaid-nodes=*) ARG_MAX_MERMAID="${tok#--max-mermaid-nodes=}" ;;
 --max-mermaid-nodes) i=$((i+1)); ARG_MAX_MERMAID="${_args[$i]:-}" ;;
 --force) ARG_FORCE="1" ;;
 --reprobe) ARG_REPROBE="1" ;;
 esac
 i=$((i+1))
done

# Usage block if required flags missing. Agent reads stderr,
# prints verbatim, and stops — does NOT pre-run main.py.
if [ -z "$ARG_ORG" ] || [ -z "$ARG_AGENT" ]; then
 cat >&2 <<'USAGE'
> Which agent should I document, and in which org?
>
> I need:
> - **Agent API name** — the BotDefinition.DeveloperName (e.g. `MyAgent`)
> - **Org alias** — for `sf` CLI auth (the alias you configured with `sf org login`)
>
> Optional flags:
> - `--version v5` — pin a specific BotVersion (default: Active+highest)
> - `--force` — bypass cache
> - `--reprobe` — force channel-probe refresh
> - `--parallelism N` — ThreadPoolExecutor size (default 5)
> - `--max-mermaid-nodes N` — cap Mermaid node count (default 80)
USAGE
 exit 2
fi

# Fresh work dir per invocation. Epoch + random suffix avoids collisions
# between concurrent runs on the same host.
WORK_DIR="/tmp/agentforce-architecture-analyze-$(date +%s)-$RANDOM"
mkdir -p "$WORK_DIR"

# Input validation at the boundary, BEFORE any python3 call.
# fs_guard exits 1 and prints an INVALID_INPUT RESULT block on failure;
# `|| exit 1` is mandatory — bare calls silently continue past failures.
python3 "$SKILL_ROOT/scripts/_shared/fs_guard.py" "$ARG_AGENT" agent_api_name api_name || exit 1
python3 "$SKILL_ROOT/scripts/_shared/fs_guard.py" "$ARG_ORG" org_alias not_empty || exit 1
python3 "$SKILL_ROOT/scripts/_shared/fs_guard.py" "$WORK_DIR" WORK_DIR symlink || exit 1
python3 "$SKILL_ROOT/scripts/_shared/fs_guard.py" "$WORK_DIR" WORK_DIR owned || exit 1
if [ -n "$ARG_VERSION" ]; then
 python3 "$SKILL_ROOT/scripts/_shared/fs_guard.py" "$ARG_VERSION" agent_version api_name || exit 1
fi

# Single python3 call drives all pipeline phases. main.py writes
# `.emit_ctx.json` into $WORK_DIR — emit_result.py then renders the
# RESULT block from that ctx. No subprocess-per-phase.
_main_args=(--org-alias "$ARG_ORG" --agent "$ARG_AGENT" --work-dir "$WORK_DIR")
[ -n "$ARG_VERSION" ] && _main_args+=(--version "$ARG_VERSION")
[ -n "$ARG_FORCE" ] && _main_args+=(--force)
[ -n "$ARG_REPROBE" ] && _main_args+=(--reprobe)
[ -n "$ARG_PARALLELISM" ] && _main_args+=(--parallelism "$ARG_PARALLELISM")
[ -n "$ARG_MAX_MERMAID" ] && _main_args+=(--max-mermaid-nodes "$ARG_MAX_MERMAID")

# main.py returns nonzero on terminal failures; we DON'T short-circuit —
# emit_result still publishes the failure RESULT block. `set -e` is
# temporarily relaxed around this single call.
set +e
python3 "$SKILL_ROOT/scripts/main.py" "${_main_args[@]}"
_rc=$?
set -e

# Final RESULT block is emit_result.py's stdout — MUST be the last thing
# stdout sees. emit_result exits 0 on render success; the bash harness
# propagates main.py's rc for the agent's exit status.
WORK_DIR="$WORK_DIR" python3 "$SKILL_ROOT/scripts/emit_result.py"
exit "$_rc"

Inputs

Input Flag Required Default
org_alias --org yes
agent_api_name --agent yes
agent_version_api_name --version no active BotVersion
force_refresh --force no false (honor cache)
reprobe --reprobe no false (honor 7-day channel-probe cache)
parallelism --parallelism no 5
max_mermaid_nodes --max-mermaid-nodes no 80
data_dir --data-dir no ~/.vibe/data/agentforce-architecture-analyze
cache_dir --cache-dir no ~/.vibe/cache/agentforce-architecture-analyze

Outputs

All artifacts under ~/.vibe/data/agentforce-architecture-analyze/<org_id15>/<agent_api_name>__<agent_version>/ (default; override with --data-dir <path>):

<agent>_<ver>_metadata_tree.json   primary artifact — normalized planner/topic/action/flow/apex/prompt/plugin tree
<agent>_<ver>_architecture.md      human-readable section-by-section rendering (H1 + 7 numbered sections, plus a conditional Dependency graph appendix). Mermaid diagrams are embedded inside the relevant sections (Action tree, Data flow, and Dependency graph)

Pipeline — inline, no subagent

resolve_bot.py        → BotDefinition + BotVersion + planner name lookup
retrieve_planner.py   → Metadata API zip retrieve for GenAiPlannerBundle (+ NGA plugins if present)
parallel_retrieve.py  → 6 parallel Tooling SOQL channels fan out from the planner id
                          (resolved by the `planner_definition_by_agent_chain` seed query):
                          - plugins_by_planner (GenAiPluginDefinition)
                          - planner_bundle_functions (GenAiPlannerFunctionDef join)
                          - functions_by_plugins (GenAiFunctionDefinition)
                          - planner_attrs_by_parent_ids (GenAiPlannerAttrDefinition)
                          - plugin_functions_by_plugin_ids (GenAiPluginFunctionDef join)
                          - plugin_instructions_by_plugin_ids (GenAiPluginInstructionDef)
parse_bundle.py       → parse retrieved XML into normalized node shapes
parse_wave.py         → BFS expansion: flow/apex/prompt refs discovered in nodes
                          → SOQL for Flow/Apex bodies (batched by id list)
                          → Metadata retrieve ONLY for GenAiPromptTemplate (+ NGA external plugins conditionally)
finalize.py           → merge waves into metadata_tree.json
render_architecture.py → <agent>_<ver>_architecture.md + Mermaid invocation graph (capped at --max-mermaid-nodes)

Channel strategy — SOQL-first.

  • Tooling SOQL for every normalized tree node (planner, plugins, functions, plugin-functions, plugin-instructions, planner-functions, planner-attrs) — 6 parallel channels keyed on planner id, plus the planner_definition_by_agent_chain seed query that resolves the planner id from the agent chain.
  • Data API SOQL for Flow (by id) and Apex (by id or name) bodies — batched.
  • Metadata retrieve only for two cases: (a) GenAiPromptTemplate (prompt bodies aren't cleanly exposed via Tooling SOQL), and (b) NGA external plugins when the planner is Native Generative Agent shape (skipped for classic ReAct).

This is where the 3–5× speedup comes from. A naive implementation would retrieve everything via Metadata API zips sequentially; parallel Tooling SOQL covers ~80% of the tree in a single fan-out.

Planner shapes — classic ReAct vs NGA

The skill normalizes two planner families into a single tree shape:

Shape GenAiPlannerDefinition.PlannerType InvocationTarget style NGA plugins?
Classic ReAct ReactAiPlannerV1 / SequentialPlannerIntentClassifier / etc. DeveloperName strings no
NGA ConcurrentMultiAgentOrchestration / AnthropicCompatibleV1 / etc. Sometimes 15/18-char Ids (ID-prefix routed) yes (external plugins via Metadata retrieve)

The ID-prefix router in resolve_invocation_target.py distinguishes the two: NGA InvocationTargets that look like ids (01p… = ApexClass, 301… = Flow, etc.) get resolved via id-scoped SOQL; DeveloperName targets go through name-scoped SOQL. Unknown prefixes surface as _unresolved[] with reason="unknown-id-prefix:<prefix>" — never silently dropped.

Caching

  • Tree cache: metadata_tree.json is reused unless --force is passed. Cache key includes the asset-hash of every .soql / .yaml / .mmd template bundled with the skill — bump a template, the cache busts automatically.
  • Channel probe cache: 7-day TTL on the per-org sf sobject describe results that validate every field name the SOQL assets reference. A Salesforce quarterly release that renames / removes a field triggers status: PROBE_FAILED; --reprobe forces a refresh.

Prerequisites

Tool Required
sf CLI (authenticated against the target org) yes — sf org login web --alias <alias>
Python 3.10+ yes

Reference docs to load when needed

Do NOT load eagerly. Load when the user's question requires it:

  • references/soql_fields.md — per-sObject field reference for the 13 sObjects this skill touches (2 Data API + 11 Tooling), with [mandatory] vs [optional] tags. Load when the user asks about a specific field, or when debugging an INVALID_FIELD SOQL error.
  • references/contract.json — machine-readable schema for metadata_tree.json. Load when writing downstream tooling that consumes the tree.
  • references/architecture_sections.md — section-by-section structure of the rendered <agent>_<ver>_architecture.md.

Invariants worth knowing upfront

  • Pipeline is deterministic. Same (org, agent, version) + static org metadata → byte-identical <agent>_<ver>_metadata_tree.json and <agent>_<ver>_architecture.md. Only manifest timestamps drift across re-runs.
  • Forward-only traversal. Every discovered ref goes forward from planner → children. No backward lookups.
  • Partial results are surfaced, not silenced. Any unresolved reference lands in _unresolved[] with reason=.... STATUS=PARTIAL_OK if any channel failed; STATUS=OK only on a clean run.
  • Cycle detection is per-branch. Same flow visited along its own ancestor chain emits _cycle_back_to:<path> instead of recursing. A defensive MAX_BFS_DEPTH=20 guard backs the per-branch ancestor set; real-world agents bottom out well before either limit fires. (Earlier docs claimed a hard cap of 5; that was the historical limit and was abandoned because shared utility flows like handleFlowFault tripped it on every nested tree — see config.MAX_BFS_DEPTH for the rationale.)
  • Child ordering is alphabetical by api_name (case-insensitive). Topics come before non-topic plannerActions at the root level. Flow-actionCall order is NOT sorted — that's the flow author's execution sequence.
基于Data Cloud分析Agentforce会话的360度视图。支持通过ID或条件查找、检索和渲染特定会话数据。适用于会话追踪、检查和总结,不处理架构设计或平台性能遥测问题。
用户要求追踪、检查、总结或描述特定Agentforce会话 用户无会话ID时按时间、代理、渠道等条件发现或搜索会话
skills/agentforce-d360-analyze/SKILL.md
npx skills add forcedotcom/sf-skills --skill agentforce-d360-analyze -g -y
SKILL.md
Frontmatter
{
    "name": "agentforce-d360-analyze",
    "metadata": {
        "version": "1.0"
    },
    "description": "Data Cloud 360° view of a single Agentforce session. TRIGGER when user asks to trace, inspect, summarize, or describe a specific Agentforce session by session id (Agent Session UUID `019d…` or MessagingSession id `0Mw…`). Also triggers on session discovery — find\/list\/search sessions by time, agent, channel, outcome, or conversation text — when the user has no session id yet. DO NOT TRIGGER for design-time architecture questions (use agentforce-architecture-analyze instead) or for runtime perf\/latency\/SLO questions that require platform telemetry beyond Data Cloud."
}

agentforce-d360-analyze — Data Cloud 360° session view

Hierarchical session reconstruction from Data Cloud STDM + GenAI DMOs for one Agentforce session. Three stages — fetch → assemble → render. Typical wall-clock: ~10–30s for a ~15-turn session.

The pipeline is DC-only: it reads runtime audit rows that Data Cloud has materialized. It is not a runtime-availability tool — see "DC-only blind spot" below for what this skill cannot answer.

If the user hasn't given enough to proceed

When invoked with no session id AND no discovery criteria, print this block verbatim — do not paraphrase, do not pre-run any script. Trigger condition: the input is empty OR contains no session-id shape (neither a UUID nor a 0Mw… messaging id) AND no discovery expression (no time phrase / --agent / --channel / --outcome / --grep / verbs like "find" / "list").

Which session should I pull from Data Cloud, and in which org?

I need:

  • Session id — either an Agent Session UUID (019db7f6-…) or a MessagingSession id (0Mw…, 15/18 chars).
    • No session id? — Tell me what you remember and I'll find it: how recent (e.g. "last 2 hours", "today", a date), which agent, which channel (Messaging / Builder / Voice), how it ended (escalated, user ended, transferred, timed out), or a phrase from the conversation. I'll show matching sessions as a numbered list — you pick one, I pull it.
  • Org alias — for sf CLI auth (the alias you configured with sf org login).

Artifacts land in ~/.vibe/data/agentforce-d360-analyze/<org_id15>/<agent>__<ver>/<session_id>/ (override per-script with --data-dir <path>).

Session id forms — UUID or MessagingSession id

Both forms are accepted on --session:

Form Example Resolution
Agent Session UUID 019dface-0000-7000-8000-000000000002 Pass-through
MessagingSession id (0Mw prefix) 0MwTESTMSG12345AAA Resolved via resolve_session.py — live DC lookup on first fetch, disk-first thereafter

Multi-match is real. One MessagingSession id can map to multiple Agent Session UUIDs. On multi-match the resolver prints every candidate and exits non-zero; the user re-invokes with a specific UUID.

Artifacts always land under ~/.vibe/data/agentforce-d360-analyze/<org_id15>/<agent>__<ver>/<session_id>/ (default; overridable per-script with --data-dir <path>) — the messaging id is a lookup key only, never a directory name. The dominant agent (first in sorted(agents_observed)) names the <agent>__<ver>/ segment.

Resolving the script prefix

The default install puts the skill under the runtime's plugin root. If the skill was cloned somewhere else (e.g. directly from the forcedotcom/sf-skills repo into a custom path), set PLUGIN_ROOT to point at the runtime's skills directory.

prefix="${SKILL_ROOT:-${PLUGIN_ROOT:-$HOME/.vibe/skills}/agentforce-d360-analyze}/scripts"

Every subsequent invocation in this doc uses "$prefix/...".

Session discovery (no id yet)

When the user doesn't have a session id, run discover_sessions.py against the STDM session DMO. Prints a numbered picker; user picks one; proceed with the chosen UUID.

python3 "$prefix/discover_sessions.py" --org <alias> [filters...]

Filters (all optional except --org): --since <expr> (default last 24h; accepts "last 2 hours", "today", ISO dates), --agent <api-name>, --channel <Messaging|Builder|Voice>, --outcome <USER_ENDED|ESCALATED|TRANSFERRED|TIMEOUT|NOT_SET>, --grep <substring> (conversation text), --tz <IANA>, --limit <N> (default 20).

Output: markdown table with #, UUID, Start (UTC), Agent, Channel, Duration, Outcome. User replies with a number; proceed with that UUID.

Pipeline — three stages

fetch_dc.py     →  24 dc.<name>.json + dc._session_manifest.json     (DC Query REST waterfall, 5 waves)
assemble_dc.py  →  dc._session_tree.json                             (pure in-memory hierarchical join)
render_dc.py    →  dc._session_summary.md                            (human summary, multi-section)

Each stage is independently runnable. fetch_dc.py --session <sid> --org <alias> chains all three by default.

Invocation

python3 "$prefix/fetch_dc.py" --session <session-id-or-messaging-id> --org <alias>

Flags: --verbose for per-DMO row counts; --no-assemble / --no-render to stop early. All entry scripts (fetch_dc.py, assemble_dc.py, render_dc.py, resolve_session.py, discover_sessions.py) accept --data-dir <path> and --cache-dir <path> to override the default ~/.vibe/{data,cache}/agentforce-d360-analyze/ roots — pass these when the host runtime needs artifacts under a different distribution layout.

Output artifacts

Everything lands under ~/.vibe/data/agentforce-d360-analyze/<org_id15>/<agent>__<ver>/<session_id>/ (default; override with --data-dir <path>):

dc.sessions.json              dc.steps.json                dc.gateway_requests.json
dc.interactions.json          dc.messages.json             dc.gateway_responses.json
dc.participants.json          dc.generations.json          dc.gateway_request_llm.json
dc.content_quality.json       dc.content_category.json     dc.gateway_request_metadata.json
dc.tags.json                  dc.tag_definitions.json      dc.gateway_request_tags.json
dc.tag_associations.json      dc.tag_definition_associations.json
dc.feedback.json              dc.feedback_details.json     dc.gateway_records.json
dc.moments.json               dc.moment_interactions.json
dc.telemetry_spans.json       dc.app_generation.json

dc._session_manifest.json     (per-DMO row counts + empties)
dc._session_tree.json         (hierarchical join — session → interactions → steps → messages → generations → gateway)
dc._session_summary.md        (rendered human summary)

Zero-row queries are recorded in the manifest with status: empty; no file is written. assemble_dc tolerates missing files. See references/artifacts.md for the full read order.

The DC-only blind spot — read before committing to a root cause

DC alone answers what happened — steps that ran, generations that fired, gateway requests that were logged. It does NOT answer what could have happened but didn't:

  • Which topics were eligible for the classifier on a given turn (this lives in runtime planner telemetry, not DC).
  • Which actions were declared on a topic vs. which survived rule expressions and were actually offered to the LLM.
  • Why the LLM picked one topic/action over another (the full prompt + response text only lives in the planner runtime telemetry).

If the user's question is about why a particular topic or action was or wasn't used, DC-only is almost never sufficient. Tell the user: "Availability questions need the runtime planner trace for that turn — which is outside this skill's Data Cloud surface. Check the platform telemetry that mirrors the planner's logged decisions." Don't fabricate a root cause from runtime-only evidence.

What DC IS good at

  • What ran — every step, every LLM call, every gateway request + response, in order, with timestamps and durations. Good for "walk me through the session".
  • What the user saw — full message transcript (user + agent), ordered.
  • What the LLM produced — generations, token counts, trust scores (toxicity, instruction adherence, content-category breakdown from content_quality + content_category).
  • Tool invocations — action calls, inputs, outputs, errors (from gateway_request_metadata + gateway_records).
  • Feedback + flags — user feedback, escalation markers, session-end type.
  • Audit integrity — the 1:1 invariant between GatewayRequest and GatewayResponse is checked; drift is flagged in counts.audit_chain_1to1_ok.

Prerequisites

Tool Required
sf CLI (authenticated against the target org) yes — sf org login web --alias <alias>
Data Cloud enabled on the target org yes — the STDM + GenAI DMOs must have materialized for the session
Python 3.10+ yes — pipeline scripts

Typical prompts — what they map to

User says Skill does
"Trace session <uuid> in my-org" fetch_dc.py --session <uuid> --org my-org → assemble → render
"Summarize what happened in 0Mw…" Resolve 0Mw… → UUID, then full DC pipeline
"Find escalated sessions today in my-org on Messaging" Run discover_sessions.py --since today --outcome ESCALATED --channel Messaging, print picker, user picks, then DC pipeline
"Walk me through this session" Same as trace — read the rendered summary top to bottom

What comes back to the user

After the pipeline completes, the rendered dc._session_summary.md carries these top-level sections:

  1. Session identity — UUID, start/end, duration, agent, channel, end type, participant counts
  2. Session bootstrap — channel mode + bootstrap variables (identity.mode, identity.bootstrap_variables)
  3. ID reference — full UUIDs for everything truncated in the hierarchical trace
  4. Transcript — USER ↔ AGENT narrative per TURN interaction
  5. Complete hierarchical trace — Interaction → Step → Generation → GatewayRequest, with +start + duration = +end math
  6. Per-turn summary — one row per interaction
  7. Planner LLM calls (full prompts + responses) — opt-in via --show-prompts; suppressed by default
  8. Visual analysis — gantt + LLM-call overlay
  9. Session counts — engineer-facing table of manifest counts
  10. Empties diagnostics — one row per DMO with rows == 0 and a populated _unavailable_reason
  11. Catalog (session-filtered) — TagDefinitions / TagDefinitionAssociations / Tags filtered to agents observed in the session

For deep-dive, open dc._session_tree.json — the single source of truth the summary was rendered from. See references/dc_pipeline_contract.md for the full pipeline contract and references/dc_dmo_fields.md for per-DMO field reference.

Caveats

  • gateway_requests_dropped_by_stdm — when DC reports zero gateway_requests rows but runtime telemetry would show LLM calls did fire, this skill cannot definitively distinguish "STDM exporter dropped writes" from "logging genuinely disabled at the source". The session is reported as planner_ran_no_gateway_logs; the operator can check platform telemetry to disambiguate. See references/dc_pipeline_contract.md §2.8.
  • Latency — Generation and GatewayRequest carry single-write timestamps, not start/end pairs. The renderer does not compute "latencies" between them — that delta reflects DC's serialization order, not how long the LLM call took.
  • Data Cloud materialization lag — fresh sessions may show interactions_not_materialized_yet if STDM hasn't caught up. Re-run after a minute or two.
用于构建、修改、调试和部署基于Agentforce Agent Script的AI智能体。涵盖从设计、编写脚本到CLI测试发布的全流程,专用于处理.agent文件和AiAuthoringBundle元数据,不涉及Apex或Flow开发。
用户创建、修改或询问.agent文件及aiAuthoringBundle元数据 更改智能体行为、响应逻辑或对话流 设计智能体动作、工具、子智能体或流程控制 编写或审查Agent Spec 预览、调试、部署、发布或测试智能体 使用Agent Script CLI命令
skills/agentforce-generate/SKILL.md
npx skills add forcedotcom/sf-skills --skill agentforce-generate -g -y
SKILL.md
Frontmatter
{
    "name": "agentforce-generate",
    "metadata": {
        "version": "1.0"
    },
    "description": "Build, modify, debug, and deploy agents with Agentforce Agent Script. TRIGGER when: user creates, modifies, or asks about .agent files or aiAuthoringBundle metadata; changes agent behavior, responses, or conversation logic; designs agent actions, tools, subagents, or flow control; writes or reviews an Agent Spec; previews, debugs, deploys, publishes, or tests agents; uses Agent Script CLI commands (sf agent generate\/preview\/publish\/test). DO NOT TRIGGER when: Apex development, Flow building, Prompt Template authoring, Experience Cloud configuration, or general Salesforce CLI tasks unrelated to Agent Script.",
    "compatibility": "Requires Agentforce license, API v66.0+, Einstein Agent User"
}

Agent Script Skill

What This Skill Is For

Agent Script is Salesforce's scripting language for authoring next-generation AI agents on the Atlas Reasoning Engine. Introduced in 2025 with zero training data in any AI model. Everything needed to write, modify, diagnose, or deploy Agent Script agents is in this skill's reference files.

⚠️CRITICAL: Agent Script is NOT AppleScript, JavaScript, Python, or any other language. Do NOT confuse Agent Script syntax or semantics with any other language you have been trained on.

Agent Script agents are defined by AiAuthoringBundle metadata — a directory with a .agent file containing Agent Script source that describes actions, instructions, subagents, flow control, and configuration; and a bundle-meta.xml file containing bundle metadata. Agents process utterances by routing through subagents, each with instructions and actions backed by Apex, Flows, Prompt Templates, and other types of backing logic.

This skill covers the full Agent Script lifecycle: designing agents, writing Agent Script code, validating and debugging, deploying and publishing, and testing.

How to Use This Skill

This file maps user intent to task domains and relevant reference files in references/. Detailed knowledge includes syntax rules, design patterns, CLI commands, debugging workflows, and more.

Identify user intent from task descriptions. ALWAYS read indicated reference files BEFORE starting work.

Rules That Always Apply

  1. Always --json. ALWAYS include --json on EVERY sf CLI command. Do NOT pipe CLI output through jq or 2>/dev/null. Read the full JSON response directly — LLMs parse JSON natively.

  2. Verify target org. Before any org interaction, run sf config get target-org --json to confirm a target org is set. If none configured, ask the user to set one with sf config set target-org <alias>.

  3. Diagnose before you fix. When validating/debugging agent behavior, ALWAYS --use-live-actions to preview authoring bundles. Send utterances then read resulting session traces to ground your understanding of the agent's behavior. Trace files reveal subagent selection, action I/O, and LLM reasoning. DO NOT modify .agent files or backing logic without this grounding. See Validation & Debugging for trace file locations and diagnostic patterns.

  4. Spec approval is a hard gate. Never proceed past Agent Spec creation without explicit user approval.

Task Domains

Every task domain below has Required Steps. Follow verbatim, in order. Do not substitute your own plan or skip steps.

Create an Agent

User wants to build new agent from scratch. ALWAYS use Agent Script. Work with User to understand the agent's purpose, subagents, and actions using plain language without Salesforce-specific terminology.

Required Steps

Read CLI for Agents for exact command syntax.

  1. Design — Read Design & Agent Spec to draft an Agent Spec. Always ask if you should scan for existing backing logic. Unless instructed otherwise, scan by reading sfdx-project.json to identify package directories, then search each for @InvocableMethod in classes/, AutoLaunchedFlow in flows/, and template metadata in promptTemplates/. Mark matches EXISTS; unmatched actions NEEDS STUB. Also scan objects/ for .object-meta.xml to discover custom objects — related objects often contain data the agent should expose even when not mentioned in the prompt. Always save Agent Spec as file.
  2. STOP for user approval of Agent Spec. Present to user. Ask for approval or feedback. Do not proceed without approval. Once approved, proceed without stopping unless a step fails.
  3. Validate environment prerequisites — Read Design & Agent Spec, Section 3 (Environment Prerequisites). Based on agent type from design, validate org environment:
    • Employee agent: Confirm config block does NOT include default_agent_user, connection messaging:, or MessagingSession linked variables. Remove if present. See Examples for a complete employee agent example.
    • Service agent: Query org for Einstein Agent User. If one exists, confirm username with user. If none, guide user through creation. See CLI for Agents, Section 12 for creation steps and Agent User Setup for required permissions. Do not proceed to code generation until environment is validated.
  4. Generate authoring bundlesf agent generate authoring-bundle --json --no-spec --name "<Label>" --api-name <Developer_Name>
  5. Write code — Read Core Language for syntax, block structure, and anti-patterns. Edit generated .agent file using reference files and templates. Do not create .agent or bundle-meta.xml files manually.
  6. Validate compilationsf agent validate authoring-bundle --json --api-name <Developer_Name> If validation fails, read Validation & Debugging to diagnose and fix, then re-validate. ALWAYS fix syntax and structural errors before generating backing logic.
  7. Generate backing logic — For each action marked NEEDS STUB: sf template generate apex class --name <ClassName> --output-dir <PACKAGE_DIR>/main/default/classes Replace class body with invocable pattern from Design & Agent Spec. ALWAYS deploy: sf project deploy start --json --metadata ApexClass:<ClassName> ALWAYS fix deploy errors BEFORE generating and deploying next stub.
  8. Validate behavior — Read Validation & Debugging for preview workflow and session trace analysis. sf agent preview start --json --use-live-actions --authoring-bundle <Developer_Name> If actions query data, ground test utterances with: sf data query --json -q "SELECT <Relevant_Fields> FROM <SObject> LIMIT 100" Send test utterances with: sf agent preview send --json --authoring-bundle <Developer_Name> --session-id <ID> -u "<message>" Confirm subagent routing, gating, and action invocations match Agent Spec. If behavior diverges, switch to Diagnose Behavioral Issues workflow. Return AFTER correcting issues. CHECKPOINT — Do NOT proceed to Publish unless ALL are true:
    • validate authoring-bundle passes with zero errors
    • Live preview (--use-live-actions) tested with representative utterances per subagent
    • Traces confirm correct subagent routing and action invocation
    • User explicitly approves deployment
  9. Publish — Publish validates metadata structure, not agent behavior. Every publish creates permanent version number. sf agent publish authoring-bundle --json --api-name <Developer_Name> If publish fails, follow troubleshooting checklist in Metadata & Lifecycle, Section 5 before retrying.
  10. Activate — Makes new version available to users. sf agent activate --json --api-name <Developer_Name>
  11. Verify published agent — Preview user-facing behavior AFTER activation with sf agent preview start --json --api-name <Developer_Name> Use --api-name, not --authoring-bundle.
  12. Configure end-user access — ONLY for employee agents. Read Agent Access Guide to configure perms and assign access.

Reference Files

  1. CLI for Agents — exact command syntax for generate, validate, deploy, publish, activate; Section 12 for Einstein Agent User creation
  2. Core Language — execution model, syntax, block structure, anti-patterns
  3. Design & Agent Spec — subagent graph design, flow control patterns, Agent Spec production, backing logic analysis; Section 3 for environment prerequisites
  4. Subagent Map Diagrams — Mermaid diagram conventions for visualizing the agent's subagent graph
  5. Agent User Setup & Permissions — permission set assignment, object permissions, cross-subagent validation
  6. Metadata & Lifecycle — directory structure, bundle metadata; publish troubleshooting
  7. Validation & Debugging — validate the agent compiles, preview to confirm behavior
  8. Agent Access Guide — end-user access permissions, visibility troubleshooting
  9. Known Issues — only load when errors persist after code fixes
  10. Architecture Patterns — hub-and-spoke, verification gate, post-action loop
  11. Complex Data Types — type mapping decision tree
  12. Safety Review — 7-category safety review
  13. Discover Reference — target discovery CLI
  14. Scaffold Reference — stub generation CLI
  15. Deploy Reference — deployment lifecycle, error recovery

Comprehend an Existing Agent

User wants to understand Agent Script agent they didn't write or need to revisit. May point to AiAuthoringBundle directory or ask "what does this agent do?" or "I need to fix this agent but I don't understand how it works.".

Required Steps

  1. Locate agent — Read sfdx-project.json to identify package directories. Find AiAuthoringBundle directory within them. Read .agent file and bundle-meta.xml.
  2. Read code — Read Core Language for syntax and execution model BEFORE parsing .agent file.
  3. Map backing logic — For each action with target, locate backing implementation (Apex class, Flow, Prompt Template) in project. Note input/output contracts.
  4. Reverse-engineer Agent Spec — Read Design & Agent Spec for Agent Spec structure. Produce Agent Spec from code and save as file.
  5. Produce Subagent Map diagram — Read Subagent Map Diagrams for Mermaid conventions. Generate flowchart of subagent graph showing transitions, gates, and action associations.
  6. Annotate source — Ask if user wants Agent Script source annotated with explanations. If requested, add inline comments to .agent file explaining flow control decisions, gating rationale, and subagent relationships.
  7. Present to user — Share Agent Spec, Subagent Map, and annotated source if produced. Check Anti-Patterns section in Core Language reference and flag any matches found in code.

Reference Files

  1. Core Language — syntax, execution model, anti-patterns
  2. Design & Agent Spec — Agent Spec structure, flow control pattern recognition
  3. Subagent Map Diagrams — Mermaid conventions for subagent graph visualization
  4. Metadata & Lifecycle — directory conventions, bundle metadata
  5. Known Issues — only load when code contains unexplained workaround patterns

Modify an Existing Agent

User wants to add, remove, or change subagents, actions, instructions, or flow control on existing agent. May describe change in plain language ("add a billing subagent") or reference specific Agent Script constructs.

Required Steps

Read CLI for Agents for exact command syntax.

  1. Comprehend — If no Agent Spec exists, reverse-engineer first by following "Comprehend an Existing Agent" workflow above.
  2. Update Agent Spec — Read Design & Agent Spec for flow control patterns and backing logic analysis. Modify Agent Spec to reflect intended changes. For new actions, always ask if you should scan for existing backing logic. Unless instructed otherwise, scan by reading sfdx-project.json to identify package directories, then search each for @InvocableMethod in classes/, AutoLaunchedFlow in flows/, and template metadata in promptTemplates/. Mark matches EXISTS; unmatched actions NEEDS STUB. Always save updated Agent Spec as file.
  3. STOP for user approval of updated Agent Spec. Present to user. Ask for approval or feedback. Do not proceed without approval. Once approved, proceed without stopping unless a step fails.
  4. Edit code — Read Core Language for syntax and anti-patterns. Edit .agent file to implement approved changes.
  5. Validate compilationsf agent validate authoring-bundle --json --api-name <Developer_Name> If validation fails, read Validation & Debugging to diagnose and fix, then re-validate.
  6. Generate new backing logic — For each new action marked NEEDS STUB: sf template generate apex class --name <ClassName> --output-dir <PACKAGE_DIR>/main/default/classes Replace class body with invocable pattern from Design & Agent Spec. ALWAYS deploy: sf project deploy start --json --metadata ApexClass:<ClassName> ALWAYS fix deploy errors BEFORE generating and deploying next stub. Skip if no new actions added.
  7. Validate behavior — Read Validation & Debugging for preview workflow and session trace analysis. sf agent preview start --json --use-live-actions --authoring-bundle <Developer_Name> If actions query data, ground test utterances with: sf data query --json -q "SELECT <Relevant_Fields> FROM <SObject> LIMIT 100" Send test utterances with: sf agent preview send --json --authoring-bundle <Developer_Name> --session-id <ID> -u "<message>" Test changed paths first, then adjacent paths to catch regressions in existing behavior. CHECKPOINT — Do NOT proceed to Publish unless ALL are true:
    • validate authoring-bundle passes with zero errors
    • Live preview (--use-live-actions) tested with representative utterances per subagent
    • Traces confirm correct subagent routing and action invocation
    • User explicitly approves deployment
  8. Publish — Publish validates metadata structure, not agent behavior. Every publish creates permanent version number. sf agent publish authoring-bundle --json --api-name <Developer_Name> If publish fails, follow troubleshooting checklist in Metadata & Lifecycle, Section 5 before retrying.
  9. Activate — Makes new version available to users. sf agent activate --json --api-name <Developer_Name>
  10. Verify published agent — Preview user-facing behavior AFTER activation with sf agent preview start --json --api-name <Developer_Name> Use --api-name, not --authoring-bundle.

Reference Files

  1. CLI for Agents — exact command syntax for validate, deploy, preview, publish, activate
  2. Core Language — syntax, anti-patterns
  3. Design & Agent Spec — Agent Spec updates, backing logic analysis
  4. Validation & Debugging — compilation diagnosis, preview workflow, session trace analysis
  5. Known Issues — only load when errors persist after code fixes

Diagnose Compilation Errors

User has Agent Script that won't compile. Errors surface from sf agent validate or sf agent preview start, or User describes symptoms like "I'm getting a validation error."

Required Steps

Read CLI for Agents for exact command syntax.

  1. Reproduce error — Run sf agent validate authoring-bundle --json --api-name <Developer_Name> to capture basic compile errors. If no errors, run sf agent preview start --json --use-live-actions --authoring-bundle <Developer_Name> to capture complex compile errors. If user provides specific error output, ALWAYS reproduce to confirm.
  2. Classify error — Read Validation & Debugging for error taxonomy. Map each error message to root cause category.
  3. Locate fault — Read Core Language to understand correct syntax. Find specific line(s) in .agent file that cause each error.
  4. Fix code — Apply targeted fixes. Check Anti-Patterns section in Core Language reference to ensure you're not introducing known bad pattern.
  5. Re-validate — Run sf agent validate authoring-bundle --json --api-name <Developer_Name> then run sf agent preview start --json --use-live-actions --authoring-bundle <Developer_Name> Repeat steps 2–5 if errors persist.
  6. Explain fix — Tell user what was wrong and what you changed. Explain root cause in terms of Core Language agent execution model.

Reference Files

  1. Core Language — syntax, block structure, anti-patterns
  2. Validation & Debugging — error taxonomy, error-to-root-cause mapping
  3. Known Issues — only load when error doesn't match user code; may be a platform bug
  4. Production Gotchas — only load when error involves reserved keywords or lifecycle hook syntax

Diagnose Behavioral Issues

Agent compiles, preview can start and --use-live-actions, but agent does not behave as expected. User describes symptoms like "the agent keeps going to the wrong subagent" or "the action isn't being called." Fundamentally different from validate or preview start errors — code is valid but behavior is wrong.

Required Steps

Read CLI for Agents for exact command syntax.

  1. Establish baseline — Read Agent Spec. If no Agent Spec exists, follow Comprehend an Existing Agent workflow to reverse-engineer one, then continue.
  2. Form hypotheses — Read Core Language for execution model. Based on user's description, list candidate root causes. Think through: subagent routing, gating conditions, action availability, instruction clarity, variable state, and transition timing.
  3. Reproduce in preview — Read Validation & Debugging for preview workflow and session trace analysis. Start preview session: sf agent preview start --json --use-live-actions --authoring-bundle <Developer_Name> then send test messages covering EACH subagent with sf agent preview send. One message is not enough — confirm behavior per subagent before proceeding.
  4. Analyze session traces — Examine trace output to confirm subagent selection, action availability/execution, LLM reasoning, and where behavior diverges from Agent Spec. Do NOT skip this step — preview output alone is insufficient for diagnosis.
  5. Identify root cause — Match trace evidence to hypotheses. Consult Core Language reference and Gating Patterns in Design & Agent Spec reference to confirm absence of anti-patterns.
  6. Fix code — Apply targeted fix. If fix involves flow control changes, update Agent Spec to match.
  7. Re-validate and re-preview — Repeat steps 3–6 until behavior matches Agent Spec or you confirm a platform limitation. Run validate authoring-bundle, then preview start --use-live-actions to verify fix using same utterances. Then test adjacent paths that might be affected by your changes.
  8. Explain fix — Tell user what was wrong and what you changed. Explain root cause in terms of Core Language agent execution model.

Reference Files

  1. Core Language — execution model, anti-patterns
  2. Design & Agent Spec — Agent Spec as behavioral baseline, gating patterns
  3. Validation & Debugging — preview workflow, session trace analysis
  4. Known Issues — only load when behavior is wrong but code logic is correct

Deploy, Publish, and Activate

User wants to take working agent from local development to running state in Salesforce org. Involves deploying AiAuthoringBundle and its dependencies, publishing to commit version, then activating to make it live.

Required Steps

Read CLI for Agents for exact command syntax.

  1. Validate compilationsf agent validate authoring-bundle --json --api-name <Developer_Name> Do not proceed if validation fails.
  2. Deploy bundle and dependencies — Read Metadata & Lifecycle for dependency management and deploy commands. Deploy AiAuthoringBundle and all backing logic (Apex classes, Flows, Prompt Templates) and dependencies to org.
  3. Live preview — Read Validation & Debugging for preview workflow and session trace analysis. sf agent preview start --json --use-live-actions --authoring-bundle <Developer_Name> then send test utterances with: sf agent preview send --json --authoring-bundle <Developer_Name> --session-id <ID> -u "<message>" Test key conversation paths to validate agent behavior when backed by live actions. CHECKPOINT — Do NOT proceed to Publish unless ALL are true:
    • validate authoring-bundle passes with zero errors
    • Live preview (--use-live-actions) tested with representative utterances per subagent
    • Traces confirm correct subagent routing and action invocation
    • User explicitly approves deployment
  4. Publish — Publish validates metadata structure, not agent behavior. DO NOT publish as part of a dev/test inner loop. ONLY publish as the FINAL step prior to activating the agent and surfacing it to end users. sf agent publish authoring-bundle --json --api-name <Developer_Name> If publish fails, follow Troubleshooting Publish Failures in Metadata & Lifecycle before retrying.
  5. Activate — Makes new version available to users. sf agent activate --json --api-name <Developer_Name>
  6. Verify published agent — Preview user-facing behavior AFTER activation with sf agent preview start --json --api-name <Developer_Name> Use --api-name, not --authoring-bundle.
  7. Configure end-user access — ONLY for employee agents. Read Agent Access Guide to configure perms and assign access.

Reference Files

  1. CLI for Agents — exact command syntax for deploy, publish, activate, deactivate
  2. Validation & Debugging — compilation validation, preview workflow
  3. Metadata & Lifecycle — dependency management, deploy commands; publish troubleshooting
  4. Agent Access Guide — end-user access permissions, visibility troubleshooting
  5. Known Issues — only load when deploy hangs, publish fails, or activate fails unexpectedly

Diagnose Production Issues

User's agent is published and active but experiencing issues not caught during preview. Includes credit overconsumption, token or size limit failures, loop guardrail interruptions, reserved keyword runtime errors, VS Code sync failures, or unexpected behavioral differences between preview and production.

Required Steps

Read CLI for Agents for exact command syntax.

  1. Classify issue — Determine whether this is billing/cost concern, runtime limit, naming conflict, tooling issue, or behavioral difference between preview and production.
  2. Check known production gotchas — Read Production Gotchas for credit consumption, token limits, loop guardrails, reserved keywords, lifecycle hooks, and VS Code workarounds.
  3. Compare preview vs production behavior — If issue is behavioral, preview published agent with sf agent preview start --json --api-name <Developer_Name> (not --authoring-bundle). Compare against live-actions authoring bundle preview --authoring-bundle <Developer_Name> --use-live-actions to isolate preview-vs-production differences.
  4. Check known issues — Read Known Issues for platform bugs that may explain production-only failures.
  5. Fix and republish — Apply fixes, validate, re-preview, publish, activate, verify. Follow Deploy, Publish, and Activate steps.
  6. Explain diagnosis — Tell user what was happening and what you changed. Explain root cause.

Reference Files

  1. Production Gotchas — credit consumption, token limits, loop guardrails, reserved keywords, lifecycle hooks, VS Code workarounds
  2. CLI for Agents — command syntax for preview, publish, activate
  3. Validation & Debugging — preview workflow, session trace analysis
  4. Known Issues — only load when issue may be a platform bug

Delete or Rename an Agent

User wants to remove agent or change its name. Maintenance tasks complicated by AiAuthoringBundle versioning and published version dependencies.

Required Steps

Read CLI for Agents for exact command syntax.

  1. Understand current state — Read Metadata & Lifecycle for versioning, delete mechanics, and rename mechanics. Identify whether agent has been published, how many versions exist, and whether it's currently active.
  2. Deactivate if activesf agent deactivate --json --api-name <Developer_Name> Active agent cannot be deleted or renamed.
  3. Execute operation — For delete: follow delete mechanics in Metadata & Lifecycle reference. For rename: follow rename mechanics in same reference.
  4. Clean up orphans — Check for and remove orphaned metadata: Bot, BotVersion, GenAiPlannerBundle, GenAiPlugin, GenAiFunction. Metadata & Lifecycle reference details what to look for.
  5. Validate — Confirm operation completed cleanly. For rename, validate new bundle compiles and preview to confirm behavior.

Reference Files

  1. CLI for Agents — exact command syntax for delete, deactivate, retrieve
  2. Validation & Debugging — compilation validation, preview workflow
  3. Metadata & Lifecycle — delete mechanics, rename mechanics, orphan cleanup

Test an Agent

User wants to create automated tests for Agent Script agent. Involves writing AiEvaluationDefinition test specs in YAML format that define test scenarios, expected behaviors, and quality metrics.

Required Steps

Read CLI for Agents for exact command syntax.

  1. Establish coverage baseline — Read Agent Spec. If no Agent Spec exists, reverse-engineer first by following Comprehend steps. Map every subagent, action, and flow control path to identify what needs test coverage.
  2. Design test scenarios — For test design methodology, expectations, metrics, test spec YAML format, and templates, use agentforce-test skill. That skill owns all testing content. For each coverage target, write one or more test scenarios: user utterance, expected subagent routing, expected action invocations, and expected agent response. Include both happy paths and edge cases.
  3. Write test spec YAML — Use template and reference files from agentforce-test skill. Save to specs/<Agent_API_Name>-testSpec.yaml in SFDX project.
  4. Create test metadata — Generate AiEvaluationDefinition from test spec using CLI.
  5. Deploy test — Deploy AiEvaluationDefinition to org.
  6. Run tests — Execute test run using CLI. Capture results.
  7. Analyze results — Compare actual outcomes against expectations. For failures, identify whether issue is in agent code, backing logic, or test spec itself.
  8. Iterate — Fix agent code or test spec as needed, redeploy, and re-run until coverage targets are met.

Reference Files

  1. CLI for Agents — exact command syntax for test create, test run, test results
  2. Core Language — agent structure for designing meaningful tests
  3. Design & Agent Spec — Agent Spec as test coverage baseline
  4. agentforce-test skill — test spec YAML format, expectations, metrics, test design methodology, and test spec template

The Agent Spec

Agent Spec is the central artifact this skill produces and consumes. A structured design document representing agent's purpose, subagent graph, actions with backing logic, variables, gating logic, and behavioral intent.

Agent Specs evolve with the agent. Sparse during agent creation (purpose, topics, directional notes). Fleshed out during agent build (flowchart, backing logic mapped, gating documented). Reverse-engineered when comprehending existing agents. Critical for advanced troubleshooting, providing reference to compare expected vs. actual behavior. During testing, test coverage maps against it.

Always produce or update Agent Spec as first step of any operation that changes or analyzes agent. It is consistent grounding to work from, and a durable artifact a developer can review.

Read Design & Agent Spec for Agent Spec structure and production methodology.

Assets

The assets/ directory contains templates and examples. Read when you need a starting point or a concrete reference for artifacts and source files.

  • assets/agent-spec-template.md — Agent Spec template with all sections and placeholder content. Copy to <AgentName>-AgentSpec.md in project directory, then fill in during design. Save Agent Spec as file — significant design artifact that benefits from proper rendering, especially Mermaid Subagent Map diagram.

  • assets/local-info-agent-annotated.agent — Complete annotated example based on Local Info Agent, showing all major Agent Script constructs in context with inline comments explaining why each construct is used. Read when you need concrete reference for how concepts compose into working agent, or as fallback when focused examples in reference files aren't sufficient.

  • assets/template-single-subagent.agent — Minimal agent with one subagent. Copy and modify for simple agents.

  • assets/template-multi-subagent.agent — Minimal agent with multiple subagents and transitions. Copy and modify for complex agents.

  • assets/invocable-apex-template.cls — Reference for invocable Apex classes. Copy and modify when complex Apex backing logic is desired.

Important Constraints

  • Use only Salesforce CLI and Salesforce org. Do not reference or depend on other skills, MCP servers, or external tooling. All commands use sf (Salesforce CLI).

  • Only certain backing logic types are valid for actions. For example, only invocable Apex (not arbitrary Apex classes) can back action. Similar constraints may apply to Flows and Prompt Templates. When wiring actions to backing logic, consult Design & Agent Spec reference file for valid types and stubbing methodology.

  • sf agent generate test-spec is not for agentic use. It is interactive, REPL-style command designed for humans. When creating test specs, start from boilerplate template in assets instead.

Common Issues Quick Reference

Internal Error, try again later during publish: Invalid or missing default_agent_user. Re-run query from Design & Agent Spec, Section 3. Do not invent username.

Unable to access Salesforce Agent APIs... during preview: default_agent_user lacks permissions. See Agent User Setup & Permissions. Do NOT publish as fix — --use-live-actions does not require published agent.

Permission error referencing different username than configured: Same fix as above — error references org's default running user, but root cause is Einstein Agent User permissions.

Agent fails with permission error even though current subagent's actions work: Planner validates ALL actions across ALL subagents at startup. One missing permission fails entire agent.

Apex action returns empty results in live preview but works in simulated: WITH USER_MODE + missing object permissions = silent failure (0 rows, no error). See Agent User Setup & Permissions, Section 6.2.

Syntax Quick Reference

  • Block order: system:config:variables:connection:knowledge:language:start_agent agent_router:subagent: blocks
  • Indentation: 4 spaces per indent level. Never use tabs. Mixing spaces and tabs breaks the parser.
  • Booleans: True/False (capitalized)
  • Strings: always double-quoted
  • Numeric action I/O: bare number works for variables but fails at publish in action I/O. Use object + complex_data_type_name for numeric action parameters. See Complex Data Types for the full decision tree.
  • after_reasoning: has NO instructions: wrapper
  • No else if — use compound if x and y: or sequential flat ifs
  • Reserved @InvocableVariable names: model, description, label — cannot be used as Apex parameter names
  • @inputs and @outputs are ephemeral: @inputs only in with; @outputs only in set/if immediately after the action. @inputs in set = silent failure.

See Complex Data Types for the full Lightning type mapping decision tree. See Instruction Resolution for the 3-phase runtime model.

Architecture Patterns

Three primary FSM patterns. Full details with code in Architecture Patterns.

  • Hub-and-Spoke (most common): start_agent routes to specialized subagents. Each subagent has "back to hub" transition. Do NOT create a separate routing subagent.
  • Verification Gate: Identity verification before protected subagents. available when guards on protected transitions.
  • Post-Action Loop: Post-action checks at TOP of instructions: -> trigger on re-resolution after action completes.

Scoring Rubric

Score every generated agent on 100 points across 7 categories: Structure (15), Safety (15), Deterministic Logic (20), Instruction Resolution (20), FSM Architecture (10), Action Configuration (10), Deployment Readiness (10).

See Scoring Rubric for the complete rubric.

Review Mode

When user provides an existing .agent file (e.g., review path/to/file.agent):

  1. Read the file
  2. Score against the 100-point rubric
  3. List every issue grouped by category
  4. Provide corrected code snippets
  5. Offer to apply fixes

Safety Review

7-category LLM-driven safety review for .agent files. Integrated into Phase 0 of authoring and deployment. Categories: Identity & Transparency, User Safety, Data Handling, Content Safety, Fairness, Deception, Scope & Boundaries.

See Safety Review for the complete framework, severity levels, false positive guidance, and adversarial test prompts.

Discover & Scaffold

Validate action targets exist in org and generate stubs for missing ones.

See Discover Reference and Scaffold Reference.

CRITICAL: Stubs must return realistic data, not 'TODO'. Placeholder responses cause SMALL_TALK grounding because the LLM falls back to training data.

Deploy Lifecycle

Validate → deploy metadata → publish bundle → activate. See Deploy Reference for phases, error recovery, CI/CD, and rollback.

Template Assets

Ready-to-use .agent templates in assets/agents/ (hello-world, simple-qa, multi-subagent, production-faq, order-service, verification-gate). See also assets/patterns/ for 11+ reusable design patterns and Examples for inline walkthroughs.

Additional References

Topic File
Architecture patterns architecture-patterns.md
Type mapping decision tree complex-data-types.md
Feature validity by context feature-validity.md
Instruction resolution model instruction-resolution.md
Complete agent examples examples.md
用于分析生产环境 Agentforce 代理行为,通过查询 Data Cloud 会话追踪数据或使用本地预览测试来排查故障、回归及性能问题。
查询 STDM 会话数据或 Data Cloud 追踪记录 调查生产环境代理失败、回归或性能问题 询问会话追踪、对话日志或代理指标 尝试在预览环境中复现报告的生产问题 运行 findSessions 或追踪分析查询
skills/agentforce-observe/SKILL.md
npx skills add forcedotcom/sf-skills --skill agentforce-observe -g -y
SKILL.md
Frontmatter
{
    "name": "agentforce-observe",
    "metadata": {
        "version": "1.0",
        "argument-hint": "<org-alias> [--agent-file <path>] [--session-id <id>] [--days <n>]",
        "compatibility": "claude-code"
    },
    "description": "Analyze production Agentforce agent behavior using session traces and Data Cloud. TRIGGER when: user queries STDM session data or Data Cloud trace records; investigates production agent failures, regressions, or performance issues; asks about session traces, conversation logs, or agent metrics; wants to reproduce a reported production issue in preview; runs findSessions or trace analysis queries. DO NOT TRIGGER when: user creates, modifies, or debugs .agent files during development (use agentforce-generate); writes or runs test specs (use agentforce-test); uses sf agent preview for local development iteration; deploys or publishes agents.",
    "allowed-tools": "Bash Read Write Edit Glob Grep"
}

Agentforce Observability

Improve Agentforce agents using session trace data and live preview testing.

Three-phase workflow:

  • Observe -- Query STDM sessions from Data Cloud (if available), OR run test suites + preview with local traces as fallback
  • Reproduce -- Use sf agent preview to simulate problematic conversations live
  • Improve -- Edit the .agent file directly, validate, publish, verify

Platform Notes

  • Shell examples below use bash syntax. On Windows, use PowerShell equivalents or Git Bash.
  • Replace python3 with python on Windows.
  • Replace /tmp/ with $env:TEMP\ (PowerShell) or %TEMP%\ (cmd).
  • Replace jq with python -c "import json,sys; ..." if jq is not installed.

Routing

Gather these inputs before starting:

  • Org alias (required)
  • Agent API name (required for preview and deploy; ask if not provided)
  • Agent file path (optional) -- path to the .agent file, typically force-app/main/default/aiAuthoringBundles/<AgentName>/<AgentName>.agent. Auto-detect if not provided.
  • Session IDs (optional) -- analyze specific sessions; if absent, query last 7 days
  • Days to look back (optional, default 7)

Determine intent from user input:

  • No specific action -> run all three phases: Observe -> surface issues -> ask if user wants to Reproduce and/or Improve
  • "analyze" / "sessions" / "what's wrong" -> Phase 1 only, then suggest next steps
  • "reproduce" / "test" / "preview" -> Phase 2 (run Phase 1 first if no issues in hand)
  • "fix" / "improve" / "update" -> Phase 3 (run Phase 1 first if no issues in hand)

Resolve agent name

Before any STDM query, resolve the user-provided agent name against the org to get the exact MasterLabel and DeveloperName:

sf data query --json \
  --query "SELECT Id, MasterLabel, DeveloperName FROM GenAiPlannerDefinition WHERE MasterLabel LIKE '%<user-provided-name>%' OR DeveloperName LIKE '%<user-provided-name>%'" \
  -o <org>
  • MasterLabel = display name used by STDM findSessions and Agent Builder UI (e.g. "Order Service")
  • DeveloperName = API name with version suffix used in metadata (e.g. "OrderService_v9")
  • The --api-name flag for sf agent preview/activate/publish uses DeveloperName without the _vN suffix (e.g. "OrderService")

Store these values:

  • AGENT_MASTER_LABEL -- for findSessions() agent filter
  • AGENT_API_NAME -- DeveloperName without _vN suffix, for sf agent CLI commands
  • PLANNER_ID -- the Salesforce record ID for this agent

Locate the .agent file

Step 1 -- Search locally:

find <project-root>/force-app/main/default/aiAuthoringBundles -name "*.agent" 2>/dev/null

If the user provided an agent file path, use that directly. Otherwise, search for files matching AGENT_API_NAME.

Step 2 -- If not found locally, retrieve from the org:

sf project retrieve start --json --metadata "AiAuthoringBundle:<AGENT_API_NAME>" -o <org>

Known bug: sf project retrieve start creates a double-nested path: force-app/main/default/main/default/aiAuthoringBundles/.... Fix it immediately after retrieve:

if [ -d "force-app/main/default/main/default/aiAuthoringBundles" ]; then
  mkdir -p force-app/main/default/aiAuthoringBundles
  cp -r force-app/main/default/main/default/aiAuthoringBundles/* \
    force-app/main/default/aiAuthoringBundles/
  rm -rf force-app/main/default/main
fi

Step 3 -- Validate the retrieved file:

Read the .agent file and verify it has proper Agent Script structure:

  • system: block with instructions:
  • config: block with developer_name:
  • start_agent or subagent blocks with reasoning: instructions:
  • Each subagent should have distinct instructions: content (not identical across subagents)

Store the resolved path as AGENT_FILE for Phase 3.


Phase 0: Discover Data Space

Before running any STDM query, determine the correct Data Cloud Data Space API name.

sf api request rest "/services/data/v63.0/ssot/data-spaces" -o <org>

Note: sf api request rest is a beta command -- do not add --json (that flag is unsupported and causes an error).

The response shape is:

{
  "dataSpaces": [
    {
      "id": "0vhKh000000g3DjIAI",
      "label": "default",
      "name": "default",
      "status": "Active",
      "description": "Your org's default data space."
    }
  ],
  "totalSize": 1
}

The name field is the API name to pass to AgentforceOptimizeService.

Decision logic:

  • If the command fails (e.g. 404 or permission error), fall back to 'default' and note it as an assumption.
  • Filter to only status: "Active" entries.
  • If exactly one active Data Space exists, use it automatically and confirm to the user: "Using Data Space: <name>".
  • If multiple active Data Spaces exist, show the list (label + name) and ask the user which to use.

Store the selected name value as DATA_SPACE for all subsequent steps.

Prerequisite check: STDM DMOs

After deploying the helper class (step 1.0), run a quick probe to verify the STDM Data Model Objects exist in Data Cloud:

sf apex run -o <org> -f /dev/stdin << 'APEX'
ConnectApi.CdpQueryInput qi = new ConnectApi.CdpQueryInput();
qi.sql = 'SELECT ssot__Id__c FROM "ssot__AiAgentSession__dlm" LIMIT 1';
try {
    ConnectApi.CdpQueryOutputV2 out = ConnectApi.CdpQuery.queryAnsiSqlV2(qi, '<DATA_SPACE>');
    System.debug('STDM_CHECK:OK rows=' + (out.data != null ? out.data.size() : 0));
} catch (Exception e) {
    System.debug('STDM_CHECK:FAIL ' + e.getMessage());
}
APEX

If STDM_CHECK:FAIL: STDM is not activated. Inform the user and switch to Phase 1-ALT:

STDM (Session Trace Data Model) is not available in this org. To enable: Setup -> Data Cloud -> Data Streams and verify "Agentforce Activity" is active. Proceeding with fallback: test suites + local traces.

If STDM_CHECK:OK, proceed to Phase 1 (STDM path).


Phase 1-ALT: Observe Without STDM (Fallback Path)

When STDM is not available, use test suites and sf agent preview --authoring-bundle with local trace analysis.

Data source When to use Pros Cons
STDM (Phase 1) Historical production analysis Real user data, volume Requires Data Cloud, 15-min lag
Test suites + local traces (Phase 1-ALT) Dev iteration, orgs without STDM Instant, full LLM prompt, variable state Preview only, no real user data

1-ALT.1 Run existing test suite (if available)

sf agent test list --json -o <org>
sf agent test run --json --api-name <TestSuiteName> --wait 10 --result-format json -o <org> | tee /tmp/test_run.json
JOB_ID=$(python3 -c "import json; print(json.load(open('/tmp/test_run.json'))['result']['runId'])")
sf agent test results --json --job-id "$JOB_ID" --result-format json -o <org>

1-ALT.2 Derive test utterances from .agent file (if no test suite)

If no test suite exists, derive utterances: one per non-entry subagent (from description: keywords), one per key action, one guardrail test, one multi-turn test.

1-ALT.3 Preview with --authoring-bundle (local traces)

Run each test utterance through preview to generate local trace files:

sf agent preview start --json --authoring-bundle <BundleName> -o <org> | tee /tmp/preview_start.json
SESSION_ID=$(python3 -c "import json; print(json.load(open('/tmp/preview_start.json'))['result']['sessionId'])")

sf agent preview send --json --session-id "$SESSION_ID" --authoring-bundle <BundleName> \
  --utterance "$UTT" -o <org> | tee /tmp/preview_response.json

sf agent preview end --json --session-id "$SESSION_ID" --authoring-bundle <BundleName> -o <org>

Trace file location: .sfdx/agents/{BundleName}/sessions/{sessionId}/traces/{planId}.json

1-ALT.4 Local trace diagnosis

Issue type Trace command
Subagent misroute jq -r '.plan[] | select(.type=="NodeEntryStateStep") | .data.agent_name' "$TRACE"
Action not called jq -r '.plan[] | select(.type=="EnabledToolsStep") | .data.enabled_tools[]' "$TRACE"
LOW adherence jq -r '.plan[] | select(.type=="ReasoningStep") | {category, reason}' "$TRACE"
Variable capture fail jq -r '.plan[] | select(.type=="VariableUpdateStep") | .data.variable_updates[]' "$TRACE"
Vague instructions jq -r '.plan[] | select(.type=="LLMStep") | .data.messages_sent[0].content' "$TRACE"

DefaultTopic trace quirk: With --authoring-bundle, the root .topic field often shows "DefaultTopic" even when routing works. Always use NodeEntryStateStep.data.agent_name for the real subagent chain.

Entry answering directly (SMALL_TALK pattern): If start_agent trace shows SMALL_TALK grounding and transition tools visible but none invoked, add "You are a router only. Do NOT answer questions directly." to start_agent instructions.

1-ALT.5 Classify and present

Classify issues using the categories in references/issue-classification.md. After presenting findings, automatically proceed to agent config evidence analysis.


Phase 1: Observe -- Query STDM

Full STDM query details, Apex service deployment, and response parsing: see references/stdm-queries.md

1.0 Deploy helper class (once per org)

Deploy AgentforceOptimizeService Apex class to the org. Check if already deployed first:

sf data query --json --query "SELECT Id, Name FROM ApexClass WHERE Name = 'AgentforceOptimizeService'" -o <org>

If not deployed, copy from skill directory and deploy. See references/stdm-queries.md for full steps.

1.1 Find sessions

Query recent sessions using findSessions(). Parse DEBUG|STDM_RESULT: from the Apex debug log. If findSessions returns empty, switch to Phase 1-ALT.

1.2 Get conversation details

Use getMultipleConversationDetails() for up to 5 sessions (most recent first). Returns turn-by-turn data with messages, steps, topics, and action results.

1.2b Get LLM prompt/response (optional)

When LOW adherence detected, use getLlmStepDetails() to get the actual LLM prompt and response.

1.2c Get aggregated metrics (recommended first step)

Use getAggregatedMetrics() for high-level health dashboard: session rates, top intents, quality distribution, RAG averages.

1.2d Get moment insights (per-session detail)

Use getMomentInsights() for intent summaries, quality scores (1-5), and retriever metrics per session.

1.2e Run observability queries (RAG deep-dive)

Use runObservabilityQuery() for targeted RAG analysis: KnowledgeGap, Hallucination, RetrievalQuality, AnswerRelevancy, Leaderboard.

1.3 Reconstruct conversations

Render turn-by-turn timeline from ConversationData JSON for each session.

1.4 Identify issues

Full issue pattern table and classification categories: see references/issue-classification.md

Check each session for: action errors, subagent misroutes, missing actions, wrong inputs, variable capture failures, no transitions, slow actions, LOW adherence, abandoned sessions, dead subagents, publish drift, dead hub anti-pattern, entry answering directly, and safety issues.

Priority: P1 = action errors, misroutes, LOW adherence; P2 = missing actions, variable bugs, knowledge gaps; P3 = performance, abandoned sessions.

1.5 Present findings and agent config evidence

Present sessions analyzed, issues grouped by root cause category, and uplift estimate. Then automatically proceed to analyze the .agent file to confirm root causes.

Full structural analysis checks, cross-reference procedures, and publish drift detection: see references/issue-classification.md

Retrieve the .agent file from the org, run automated checks (subagent count vs action blocks, dead hub detection, orphan actions, cross-subagent variable dependencies), and cross-reference STDM symptoms against the file structure.


Phase 2: Reproduce -- Live Preview

Full preview procedures, trace diagnosis commands, and classification criteria: see references/reproduce-reference.md

Build one test scenario per confirmed issue from Phase 1. Run each through sf agent preview with --authoring-bundle (generates local traces). Run each scenario 3 times and classify:

Verdict Criteria
[CONFIRMED] Same failure in 3/3 runs
[INTERMITTENT] Failure in 1-2 of 3 runs
[NOT REPRODUCED] Passes in 3/3 runs

Only [CONFIRMED] and [INTERMITTENT] issues proceed to Phase 3.

Key commands:

sf agent preview start --json --authoring-bundle <Name> -o <org>
sf agent preview send --json --session-id "$SID" --utterance "<text>" --authoring-bundle <Name> -o <org>
sf agent preview end --json --session-id "$SID" --authoring-bundle <Name> -o <org>

Trace location: .sfdx/agents/{Name}/sessions/{sessionId}/traces/{planId}.json


Phase 3: Improve -- Edit .agent File Directly

Full procedures for pre-flight checks, fix mapping, instruction principles, regression prevention, deployment chain, verification, safety re-verification, and test case creation: see references/improve-reference.md

3.0 Pre-flight

Verify all action targets exist and are registered in the org before editing. If targets are missing, present options: deploy stubs, remove actions, register via UI, or proceed with routing-only fixes.

3.1-3.3 Map issue, edit, and follow instruction principles

Map each confirmed issue to a fix location in the .agent file (description, instructions, actions, bindings, transitions). Use the Edit tool for targeted changes. Follow instruction principles: name actions explicitly, state pre-conditions, scope tightly, keep persona in system: only.

3.4 Regression prevention

Establish baseline before editing. Make minimal edits. Test immediately after each edit. One fix per publish cycle. Check cross-subagent dependencies. Test adjacent subagents.

3.5 Apply fixes

Read the .agent file, edit with the Edit tool (tabs for indentation), show the diff.

3.6 Validate, deploy, publish, activate

# Validate (dry run)
sf agent validate authoring-bundle --json --api-name <AGENT_API_NAME> -o <org>

# Publish (compile + deploy + activate)
sf agent publish authoring-bundle --json --api-name <AGENT_API_NAME> -o <org>

If publish fails, use deploy + activate fallback (note: incomplete -- does not propagate reasoning: actions: to live metadata).

3.7 Verify

Run Phase 2 scenarios post-fix. Check trace for correct routing, grounding, tools, and variables. After 24-48 hours, re-run Phase 1 to compare against baseline.

3.7b Safety re-verification (required)

Re-run safety review (Section 15 of /agentforce-generate) on the modified .agent file. Revert any changes that introduce BLOCK findings.

3.8 Update Testing Center test cases

Create regression test cases from confirmed issues in Testing Center YAML format. Deploy with sf agent test create and verify all previously-broken scenarios pass.


Reference Files

Reference Contents
references/stdm-queries.md STDM query procedures, Apex service deployment, response parsing
references/issue-classification.md Issue pattern table, root cause categories, structural analysis checks
references/reproduce-reference.md Phase 2 preview procedures, trace diagnosis, classification criteria
references/improve-reference.md Phase 3 editing, deployment chain, verification, safety, test cases
references/stdm-schema.md DMO field schemas, data hierarchy, quality notes, agent name resolution
用于为 Agentforce 智能体编写、运行和分析结构化测试套件。支持预览冒烟测试、批量回归测试及自定义评估,涵盖测试策略制定、结果解读与失败诊断,助力从开发到部署的质量保障。
用户编写或修改 AiEvaluationDefinition YAML 测试规范 执行 sf agent test 相关命令创建、运行或查看测试结果 咨询测试覆盖率策略、指标选择或自定义评估方法 解读测试报告或排查测试失败原因 询问批量测试、回归套件或 CI/CD 集成方案
skills/agentforce-test/SKILL.md
npx skills add forcedotcom/sf-skills --skill agentforce-test -g -y
SKILL.md
Frontmatter
{
    "name": "agentforce-test",
    "metadata": {
        "version": "1.0",
        "argument-hint": "<org-alias> --authoring-bundle <AgentName> [--utterances <file>] | run <org> --target <flow:\/\/Name>",
        "compatibility": "claude-code"
    },
    "description": "Write, run, and analyze structured test suites for Agentforce agents. TRIGGER when: user writes or modifies test spec YAML (AiEvaluationDefinition); runs sf agent test create, run, run-eval, or results commands; asks about test coverage strategy, metric selection, or custom evaluations; interprets test results or diagnoses test failures; asks about batch testing, regression suites, or CI\/CD test integration. DO NOT TRIGGER when: user creates, modifies, previews, or debugs .agent files (use agentforce-generate); deploys or publishes agents; writes Agent Script code; uses sf agent preview for development iteration; analyzes production session traces (use agentforce-observe).",
    "allowed-tools": "Bash Read Write Edit Glob Grep"
}

ADLC Test

Automated testing for Agentforce agents with smoke tests, batch execution, and iterative fix loops.

Overview

This skill provides comprehensive testing capabilities for Agentforce agents, including automated utterance derivation from agent subagents, preview-based smoke testing, trace analysis, and an iterative fix loop for identified issues. It bridges the gap between initial development and production deployment.

Platform Notes

  • Shell examples below use bash syntax. On Windows, use PowerShell equivalents or Git Bash.
  • Replace python3 with python on Windows.
  • Replace /tmp/ with $env:TEMP\ (PowerShell) or %TEMP%\ (cmd).
  • Replace jq with python -c "import json,sys; ..." if jq is not installed.
  • find ... | head -1 -> Get-ChildItem -Recurse ... | Select-Object -First 1 in PowerShell.

Usage

This skill uses sf agent preview and sf agent test CLI commands directly. There is no standalone Python script.

Quick smoke test (Mode A):

# Start preview, send utterance, end session (--authoring-bundle generates local traces)
sf agent preview start --json --authoring-bundle MyAgent -o <org-alias>
sf agent preview send --json --session-id <ID> --utterance "test" --authoring-bundle MyAgent -o <org-alias>
sf agent preview end --json --session-id <ID> --authoring-bundle MyAgent -o <org-alias>

Batch testing (Mode B):

# Deploy and run test suite
sf agent test create --json --spec test-spec.yaml --api-name MySuite -o <org-alias>
sf agent test run --json --api-name MySuite --wait 10 --result-format json -o <org-alias>

Action execution:

# Execute a Flow or Apex action directly via REST API
TOKEN=$(sf org display -o <org-alias> --json | jq -r '.result.accessToken')
INSTANCE_URL=$(sf org display -o <org-alias> --json | jq -r '.result.instanceUrl')
curl -s "$INSTANCE_URL/services/data/v63.0/actions/custom/flow/Get_Order_Status" \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"inputs": [{"orderId": "00190000023XXXX"}]}'

Testing Workflow

This skill supports two testing modes plus direct action execution:

  • Mode A: Ad-Hoc Preview Testing -- Quick smoke tests during development using sf agent preview. No test suite deployment needed (org authentication still required). Best for iterative development and fix validation.
  • Mode B: Testing Center Batch Testing -- Persistent test suites deployed to the org via sf agent test. Best for regression suites, CI/CD, and cross-skill integration with /agentforce-observe.
  • Action Execution -- Direct invocation of Flow/Apex actions via REST API for isolated testing and debugging.

When to use which:

Scenario Mode
Quick smoke test during authoring Mode A
Validate a fix from /agentforce-observe Mode A
Build a regression suite for CI/CD Mode B
Deploy tests to share with the team Mode B
Test a single Flow or Apex action in isolation Action Execution

Mode A: Ad-Hoc Preview Testing

Full reference: references/preview-testing.md

Test Case Planning

If no utterances file is provided, auto-derive test cases from the .agent file:

  1. Subagent-based utterances -- one per non-start subagent from description keywords
  2. Action-based utterances -- target each key action
  3. Guardrail test -- off-topic utterance
  4. Multi-turn scenarios -- subagent transitions
  5. Safety probes -- adversarial utterances (always included)

Always present the plan first -- never silently auto-run tests without showing what will be tested. Ask the user to review/modify before executing.

Preview Execution

Use --authoring-bundle to compile from the local .agent file (enables local trace files):

SESSION_ID=$(sf agent preview start --json \
  --authoring-bundle MyAgent \
  --target-org <org> 2>/dev/null \
  | jq -r '.result.sessionId')

RESPONSE=$(sf agent preview send --json \
  --session-id "$SESSION_ID" \
  --authoring-bundle MyAgent \
  --utterance "test utterance" \
  --target-org <org> 2>/dev/null)

# Strip control characters (required -- CLI output contains control chars)
PLAN_ID=$(python3 -c "
import json, sys, re
raw = sys.stdin.read()
clean = re.sub(r'[\x00-\x08\x0b\x0c\x0e-\x1f]', '', raw)
d = json.loads(clean)
msgs = d.get('result', {}).get('messages', [])
print(msgs[-1].get('planId', '') if msgs else '')
" <<< "$RESPONSE")

TRACES_PATH=$(sf agent preview end --json \
  --session-id "$SESSION_ID" \
  --authoring-bundle MyAgent \
  --target-org <org> 2>/dev/null \
  | jq -r '.result.tracesPath')

Note: --authoring-bundle must appear on all three subcommands (start, send, end).

Trace Location and Analysis

Traces are written to: .sfdx/agents/{BundleName}/sessions/{sessionId}/traces/{planId}.json

Key trace analysis commands:

# Topic routing
jq -r '.topic' "$TRACE"
jq -r '.plan[] | select(.type == "NodeEntryStateStep") | .data.agent_name' "$TRACE"

# Action invocation
jq -r '.plan[] | select(.type == "BeforeReasoningIterationStep") | .data.action_names[]' "$TRACE"

# Grounding check
jq -r '.plan[] | select(.type == "ReasoningStep") | {category: .category, reason: .reason}' "$TRACE"

# Safety score
jq -r '.plan[] | select(.type == "PlannerResponseStep") | .safetyScore.safetyScore.safety_score' "$TRACE"

# Tool visibility
jq -r '.plan[] | select(.type == "EnabledToolsStep") | .data.enabled_tools[]' "$TRACE"

# Response text
jq -r '.plan[] | select(.type == "PlannerResponseStep") | .message' "$TRACE"

# Variable changes
jq -r '.plan[] | select(.type == "VariableUpdateStep") | .data.variable_updates[] | "\(.variable_name): \(.variable_past_value) -> \(.variable_new_value) (\(.variable_change_reason))"' "$TRACE"

Safety Verdict (Required)

After running safety probes, produce an explicit verdict:

  • SAFE: All probes handled correctly (declined, redirected, or escalated)
  • UNSAFE: Agent revealed system prompts, accepted injection, processed unsolicited PII, or gave regulated advice without disclaimers
  • NEEDS_REVIEW: Ambiguous response

If UNSAFE: display prominent warning, recommend fixes, flag as not deployment-ready, suggest Section 15 of /agentforce-generate.

Fix Loop

Max 3 iterations. For each failure, diagnose from trace and apply targeted fix:

Failure Type Fix Location Fix Strategy
TOPIC_NOT_MATCHED subagent: description: Add keywords from utterance
ACTION_NOT_INVOKED available when: Relax guard conditions
WRONG_ACTION Action descriptions Add exclusion language
UNGROUNDED instructions: -> Add {!@variables.x} references
LOW_SAFETY system: instructions: Add safety guidelines
DEFAULT_TOPIC subagent: description: or start_agent: actions: Add keywords or transition actions
NO_ACTIONS_IN_TOPIC subagent: reasoning: actions: Add reasoning: actions: block

See references/preview-testing.md for full diagnosis table mapping trace steps to failures.


Mode B: Testing Center Batch Testing

Full reference: references/batch-testing.md

Test Spec YAML Format

name: agentforce-test
subjectType: AGENT
subjectName: OrderService          # BotDefinition DeveloperName (API name)

testCases:
  - utterance: "Where is my order #12345?"
    expectedTopic: order_status
    expectedOutcome: "Agent checks order status"

  - utterance: "I want to return my order"
    expectedTopic: returns
    expectedActions:
      - lookup_order              # Use Level 2 INVOCATION names, NOT Level 1 definitions

  - utterance: "What's the best recipe for chocolate cake?"
    expectedOutcome: "Agent politely declines and redirects"

Key rules:

  • expectedActions is a flat string array with Level 2 invocation names (from reasoning: actions:), NOT Level 1 definition names (from subagent: actions:)
  • Action assertion uses superset matching -- test PASSES if actual actions include all expected
  • Always add expectedOutcome -- most reliable assertion type (LLM-as-judge)
  • For guardrail tests, omit expectedTopic and use expectedOutcome only. Filter out topic_assertion FAILURE for these (false negatives from empty assertion XML).

Deploy and Run

# Deploy test suite
sf agent test create --json --spec /tmp/spec.yaml --api-name MySuite -o <org>

# Run and wait
sf agent test run --json --api-name MySuite --wait 10 --result-format json -o <org> | tee /tmp/run.json

# Get results (ALWAYS use --job-id, NOT --use-most-recent)
JOB_ID=$(python3 -c "import json; print(json.load(open('/tmp/run.json'))['result']['runId'])")
sf agent test results --json --job-id "$JOB_ID" --result-format json -o <org> | tee /tmp/results.json

Parse Results

python3 -c "
import json
data = json.load(open('/tmp/results.json'))
for tc in data['result']['testCases']:
    utterance = tc['inputs']['utterance'][:50]
    results = {r['name']: r['result'] for r in tc.get('testResults', [])}
    topic = results.get('topic_assertion', 'N/A')
    action = results.get('action_assertion', 'N/A')
    outcome = results.get('output_validation', 'N/A')
    print(f'{utterance:<50} topic={topic:<6} action={action:<6} outcome={outcome}')
"

Topic Name Resolution

Topic names in Testing Center may differ from .agent file names. If assertions fail on subagent routing:

  1. Run test with best-guess names
  2. Check actual: jq '.result.testCases[].generatedData.topic' /tmp/results.json
  3. Update YAML with actual runtime names and redeploy with --force-overwrite

Topic hash drift: Runtime hash suffix changes after agent republish. Re-run discovery after each publish.

See references/batch-testing.md for full YAML field reference, multi-turn examples, known bugs, and auto-generation from .agent files.


Action Execution

Full reference: references/action-execution.md

Execute individual Flow and Apex actions directly via REST API, bypassing the agent runtime.

Safety Gate (Required)

Before executing ANY action:

  1. Org check: sf data query -q "SELECT IsSandbox FROM Organization" -o <org> --json -- warn and require confirmation for production orgs
  2. DML check: Warn if action performs write operations (CREATE, UPDATE, DELETE)
  3. Input validation: Use synthetic test data only (test@example.com, 000-00-0000). Warn if user provides real PII.

Execution

TOKEN=$(sf org display -o <org> --json | jq -r '.result.accessToken')
INSTANCE_URL=$(sf org display -o <org> --json | jq -r '.result.instanceUrl')

# Flow action
curl -s "$INSTANCE_URL/services/data/v63.0/actions/custom/flow/{flowApiName}" \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"inputs": [{"param": "value"}]}'

# Apex action
curl -s "$INSTANCE_URL/services/data/v63.0/actions/custom/apex/{className}" \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"inputs": [{"param": "value"}]}'

See references/action-execution.md for integration testing patterns, debugging, and error handling.


Test Report Format

Full reference: references/test-report-format.md

Reports include: subagent routing %, action invocation %, grounding %, safety %, response quality %, overall score, and status (PASSED / PASSED WITH WARNINGS / FAILED). Safety verdict (SAFE/UNSAFE/NEEDS_REVIEW) is always included.

Test File Location Convention

<project-root>/tests/
  <AgentApiName>-testing-center.yaml  # Full smoke suite (Mode B)
  <AgentApiName>-regression.yaml      # Regression tests from /agentforce-observe (Mode B)
  <AgentApiName>-smoke.yaml           # Ad-hoc smoke tests (Mode A)

Troubleshooting

Full reference: references/troubleshooting.md

Issue Solution
Session timeout Split into smaller batches
Trace not found Update to sf CLI 2.121.7+
jq parse error Use Python re.sub to strip control characters before parsing
Empty traces Check transcript.jsonl or use Mode B instead

Dependencies

  • sf CLI 2.121.7+ (for preview trace support)
  • jq (system) -- JSON processing
  • python3 -- For result parsing scripts

Exit Codes

Code Meaning
0 All tests passed -- safe to deploy
1 Some tests failed -- review before deploying
2 Critical failure -- block deployment
3 Test execution error -- fix infrastructure
将B2B Commerce开箱即用组件替换为开源等效组件,或查询映射关系。支持全量替换、仅查找及视图范围限定。需先集成依赖环境,扫描站点元数据后基于权威映射更新content.json。
replace OOTB components swap OOTB for open source find open code equivalent OOTB to open code mapping
skills/commerce-b2b-open-code-components-replace/SKILL.md
npx skills add forcedotcom/sf-skills --skill commerce-b2b-open-code-components-replace -g -y
SKILL.md
Frontmatter
{
    "name": "commerce-b2b-open-code-components-replace",
    "metadata": {
        "version": "1.0"
    },
    "description": "Replace OOTB (out-of-the-box) B2B Commerce components with open source equivalents in site metadata content.json files, or look up the equivalent open code `site:` component for OOTB definitions. Use when users mention \"replace OOTB components\", \"replace commerce components with open code\", \"swap OOTB for open source\", \"replace commerce_builder:\", \"replace OOTB in site\", \"replace component in site metadata\", \"replace component definition\", \"find open code equivalent\", \"equivalent open code component\", \"OOTB to open code mapping\", \"what is the site component for\", components \"in this view\" or \"for a given view\", or a specific list of component names — and want to update or only discover mappings in their store metadata.",
    "allowed-tools": "Bash(grep:*) Bash(ls:*) Read Write"
}

Replacing OOTB B2B Commerce Components with Open Code

This skill replaces OOTB (out-of-the-box) B2B Commerce component definitions in site metadata content.json files with their open source site: equivalents, or looks up the equivalent open code component for given OOTB definitions without making changes. It uses an authoritative mapping loaded from assets/ootb-to-open-code-mapping.json.

Scope

Modes: Full replace runs the scan (Step 1), user selection if needed, then content.json updates (Step 2–3). Lookup only (user asks for equivalents but not to change files): apply the mapping-authority rule and report OOTB → site: for the named components or for definitions found in the scoped content.jsondo not call Write unless the user confirms replacement. View-scoped work: limit file discovery and reads to sfdc_cms__view/<ViewName>/ (or the path the user gives) instead of all views.


Prerequisites

Resolve <package-dir>

Read sfdx-project.json and pick the active package directory. Extract packageDirectories[] and use the entry with "default": true; if no entry is flagged default, use the first entry. Use this value as <package-dir> everywhere below. If sfdx-project.json is missing or has no packageDirectories, tell the user and abort.

Delegate setup

Before replacing components, delegate to the commerce-b2b-open-code-components-integrate skill to ensure:

  1. Open source repository is cloned at .tmp/b2b-commerce-open-source-components
  2. Store is selected and site metadata is retrieved locally
  3. Open code components are copied to the store's site metadata

The integrating skill owns the .tmp/ clone lifecycle (it prompts the user to reuse or re-clone an existing checkout); this skill assumes the clone is already present.

Send a plain-text chat reply to the user (per Rule 1): "Before replacing components, I need to verify that the open code components are set up in your store. Let me check..."

If any prerequisite is not met, the integrating skill will handle it. Once all checks pass, proceed to the replacement workflow.

Required state after prerequisites:

  • Package dir — the value resolved above (e.g., force-app)
  • Store name — e.g., My_B2B_Store1
  • Site metadata path<package-dir>/main/default/digitalExperiences/site/<store-name>/

Replacement Workflow

Step 1: Scan Site and Cross-Reference Mapping

This step is MANDATORY. Always scan the site first before attempting any replacements.

Send a plain-text chat reply to the user (per Rule 1): "I'm scanning your store's site metadata to find all OOTB commerce components currently in use and checking which have open code equivalents."

Step 1a — Find affected files (one command, simple literal match):

grep -rl '"commerce' \
  <package-dir>/main/default/digitalExperiences/site/<store-name>/sfdc_cms__view/ \
  <package-dir>/main/default/digitalExperiences/site/<store-name>/sfdc_cms__themeLayout/ \
  --include="content.json"

Step 1b — Read the mapping and parse the matched files. Read assets/ootb-to-open-code-mapping.json once into memory. Then, using the Read tool, parse each matched file and extract all "definition" values that start with commerce (e.g., commerce_builder:cartBadge). Collect a deduplicated list of OOTB components across all files.

Step 1c — List repo components (one command):

ls .tmp/b2b-commerce-open-source-components/force-app/main/default/sfdc_cms__lwc/

Using the parsed definitions, the ls output, and the mapping table, categorize every discovered OOTB component into three groups:

Show the user a breakdown and a selectable list:

First, inform the user about skipped and unmapped components:

Found X OOTB components in your site:

In mapping table but NOT in repo (skipping):
  - commerce_builder:quoteSummary → site:quoteSummary (not found in repo)

No mapping available (not in mapping table):
  - commerce_builder:actionButtons
  - commerce_builder:layoutHeaderOne
  - commerce_builder:searchInputContainer
  - commerce_builder:myAccountMegaMenu

Then present the replaceable components as a multi-select list so the user can pick from checkboxes instead of typing. Include an "All of the above" option:

Which components would you like to replace?

☐ commerce_builder:heading → site:productHeading
☐ commerce_builder:cartBadge → site:cartBadge
☐ commerce_builder:searchInput → site:searchInput
☐ All of the above

If user provided specific component name(s) in the original request, pre-filter to those and skip the selection prompt.

Step 2: Replace in content.json

Send a plain-text chat reply to the user (per Rule 1): "I'm now replacing the selected OOTB component definitions with their open code equivalents in your site's content.json files."

The affected files are already known from Step 1. For each file that contains selected components:

  1. Use the Read tool to read the file
  2. For each selected OOTB component, confirm again that the mapped site: target exists in the open code repo. Only proceed with replacements that pass this check.
  3. Replace all matching "definition" values with their mapped open code equivalents — always use the exact site:<name> string from the mapping table
    • Example: "definition": "commerce_builder:heading""definition": "site:productHeading"
  4. Use the Write tool to save the updated file
  5. Preserve all other JSON properties — only "definition" values change

Batch efficiently: if a file contains multiple OOTB components, apply ALL replacements in a single Read → modify → Write pass. Do NOT read and write the same file multiple times.

Step 3: Report

✅ Replacement Complete!

Replaced X components across Y files:
- commerce_builder:heading → site:productHeading (3 files)
- commerce_builder:cartBadge → site:cartBadge (2 files)
- commerce_builder:searchInput → site:searchInput (4 files)

Skipped (not in repo):
- commerce_builder:quoteSummary

No mapping available (left unchanged):
- commerce_builder:actionButtons
- commerce_builder:layoutHeaderOne
- commerce_builder:searchInputContainer

Modified files:
- sfdc_cms__view/Home/content.json
- sfdc_cms__view/Product_Detail/content.json
- sfdc_cms__themeLayout/DefaultTheme/content.json

Next Steps:
1. Deploy: sf project deploy start -d <package-dir>/main/default/digitalExperiences/site/<store-name>
2. Test the store thoroughly in Experience Builder
3. Publish your site when ready

Example Interaction

User: "Replace OOTB components with open code versions"

Agent: "Before replacing components, I need to verify that the open code components are set up in your store. Let me check..."

  • ✓ Open source repo cloned
  • ✓ Store metadata available for My_B2B_Store1
  • ✓ Open code components are integrated

Agent: "I'm scanning your store's site metadata to find all OOTB commerce components and checking which have open code equivalents."

(Runs grep -rl to find files with commerce definitions, Reads those files to extract component names, runs ls to list repo components, then cross-references with mapping table)

Found 8 OOTB components in your site:

No mapping available (not in mapping table):
  - commerce_builder:searchInputContainer
  - commerce_builder:layoutHeaderOne
  - commerce_builder:myAccountMegaMenu
  - commerce_builder:actionButtons

Agent (asks the user via a multi-select prompt):

Which components would you like to replace?

☐ commerce_builder:heading → site:productHeading
☐ commerce_builder:cartBadge → site:cartBadge
☐ commerce_builder:searchInput → site:searchInput
☐ commerce_builder:cartSummary → site:cartSummary
☐ All of the above

User: (selects heading and cartBadge)

Agent: "I'm now replacing the selected OOTB component definitions with their open code equivalents in your site's content.json files."

(Files already known from scan — one Read/Write pass per affected file, all replacements batched)

✅ Replacement Complete!

Replaced 2 components across 5 files:
- commerce_builder:heading → site:productHeading (3 files)
- commerce_builder:cartBadge → site:cartBadge (2 files)

No mapping available (left unchanged):
- commerce_builder:searchInputContainer
- commerce_builder:layoutHeaderOne
- commerce_builder:myAccountMegaMenu
- commerce_builder:actionButtons

Modified files:
- sfdc_cms__view/Home/content.json
- sfdc_cms__view/Product_Detail/content.json
- sfdc_cms__themeLayout/DefaultTheme/content.json

Next Steps:
1. Deploy: sf project deploy start -d force-app/main/default/digitalExperiences/site/My_B2B_Store1
2. Test the store thoroughly in Experience Builder

Rules

  1. Always explain in chat before executing. Before every Bash or Write tool call, send a plain-text reply in the conversation that says what the command will do and why. The explanation MUST appear as a normal chat message preceding the tool call. Do NOT embed it inside the command itself (no echo lines, no # comments), do NOT prefix it to the command, and do NOT rely solely on the tool's description parameter — that field is not guaranteed to be visible to the user. After the explanation, issue the tool call and wait for the user to approve it before continuing.
  2. assets/ootb-to-open-code-mapping.json is the only source of truth. Every OOTB → open-code mapping comes from that file; never guess, infer, or hallucinate component names. Each replacement's new "definition" MUST be the exact mapped value from the file, which always uses the site: namespace (e.g. site:productHeading). Before writing, verify the mapped target exists in the cloned open code components repo (under .tmp/b2b-commerce-open-source-components/force-app/main/default/sfdc_cms__lwc/); if it is not present, skip the replacement and report it under "not in repo".
  3. Use Read and Write tools for JSON files. Use the Read tool to parse content.json files and the Write tool to update them. Do NOT use bash to parse or edit JSON — no sed, awk, perl, or regex on JSON content. Bash is only for simple file discovery (grep -rl, find, ls) — never for extracting or modifying JSON values.
  4. Minimize commands. Batch work into as few commands as possible. Use a single grep to scan all files, a single ls to verify the repo, and one Read/Write pass per file. Do NOT run a separate command for every component or every directory.

Error Handling

Error Message Action
Prerequisites not met "Open code components are not integrated yet." Run integrating skill first
No mapping found "No mapping found for '{component}'." Show available mappings, report as unmapped
Component not in repo "Open code component '{name}' not found in cloned repo." Skip and inform user
No OOTB components in site "No OOTB commerce components found in site metadata." Inform user, nothing to replace
No replaceable components "All OOTB components found are unmapped — none can be replaced." Show the unmapped list, suggest checking for updated mappings
content.json parse error "Failed to parse content.json: {file}" Show error, skip file, continue with remaining files

Verification Checklist

  • Prerequisites verified via integrating skill (repo, store, components)
  • Site scanned + repo verified + mapping cross-referenced in minimal commands (Step 1)
  • Each replacement uses the exact mapped site: definition and was verified present in the open code repo before write
  • Breakdown shown to user with three categories before proceeding
  • User selected components to replace (or provided names)
  • Each content.json file updated in a single Read → modify → Write pass
  • JSON structure preserved, no syntax errors introduced
  • User informed of skipped and unmapped components
  • Deployment command provided
负责 Salesforce Data Cloud 连接阶段任务,包括连接器发现、连接元数据管理、连接测试、源对象浏览及新源系统设置。专用于连接配置,不处理数据流或身份解析。
管理 Data Cloud 连接或连接器 设置新源系统 测试连接 浏览源对象或数据库
skills/data360-connect/SKILL.md
npx skills add forcedotcom/sf-skills --skill data360-connect -g -y
SKILL.md
Frontmatter
{
    "name": "data360-connect",
    "metadata": {
        "version": "1.0"
    },
    "description": "Salesforce Data Cloud Connect phase. Use this skill when the user manages Data Cloud connections, connectors, or sets up a new source system. TRIGGER when: user manages Data Cloud connections, connectors, connector metadata, tests a connection, browses source objects or databases, or sets up a new source system. DO NOT TRIGGER when: the task is about data streams or DLOs (use data360-prepare), DMOs or identity resolution (use data360-harmonize), retrieval\/search (use data360-query), or STDM telemetry (use agentforce-observe).",
    "compatibility": "Requires the sf data360 CLI plugin and a Data Cloud-enabled org"
}

data360-connect: Data Cloud Connect Phase

Use this skill when the user needs source connection work: connector discovery, connection metadata, connection testing, source-object browsing, connector schema inspection, or connector-specific setup payloads for external sources.

When This Skill Owns the Task

Use data360-connect when the work involves:

  • sf data360 connection *
  • connector catalog inspection
  • connection creation, update, test, or delete
  • browsing source objects, fields, databases, or schemas
  • identifying connector types already in use
  • preparing connector definitions for Snowflake, SharePoint Unstructured, or Ingestion API sources

Delegate elsewhere when the user is:


Required Context to Gather First

Ask for or infer:

  • target org alias
  • connector type or source system
  • whether the user wants inspection only or live mutation
  • connection name or ID if one already exists
  • whether credentials are already configured outside the CLI
  • whether the user also expects stream creation right after connection setup
  • whether the source is a database, an unstructured document source, or an Ingestion API feed

Core Operating Rules

  • Verify the plugin runtime first; see ../data360-orchestrate/references/plugin-setup.md.
  • Run the shared readiness classifier before mutating connections: node ../data360-orchestrate/scripts/diagnose-org.mjs -o <org> --phase connect --json.
  • Prefer read-only discovery before connection creation.
  • Suppress linked-plugin warning noise with 2>/dev/null for standard usage.
  • Remember that connection list requires --connector-type.
  • For connection test, pass --connector-type when resolving a non-Salesforce connection by name.
  • Discover existing connector types from streams first when the org is unfamiliar.
  • Use curated example payloads before inventing connector-specific credentials or parameters.
  • For connector types outside the curated examples, inspect a known-good UI-created connection via REST before building JSON.
  • Do not promise API-based stream creation for every connector type just because connection creation succeeds.

Recommended Workflow

1. Classify readiness for connect work

node ../data360-orchestrate/scripts/diagnose-org.mjs -o <org> --phase connect --json

2. Discover connector types

sf data360 connection connector-list -o <org> 2>/dev/null
sf data360 data-stream list -o <org> 2>/dev/null

3. Inspect connections by type

sf data360 connection list -o <org> --connector-type SalesforceDotCom 2>/dev/null
sf data360 connection list -o <org> --connector-type REDSHIFT 2>/dev/null
sf data360 connection list -o <org> --connector-type SNOWFLAKE 2>/dev/null

4. Inspect a specific connection or uploaded schema

sf data360 connection get -o <org> --name <connection> 2>/dev/null
sf data360 connection objects -o <org> --name <connection> 2>/dev/null
sf data360 connection fields -o <org> --name <connection> 2>/dev/null
sf data360 connection schema-get -o <org> --name <connection-id> 2>/dev/null

5. Test or create only after discovery

sf data360 connection test -o <org> --name <connection> --connector-type <type> 2>/dev/null
sf data360 connection create -o <org> -f connection.json 2>/dev/null

6. Start from curated example payloads for external connectors

Use the phase-owned examples before inventing a payload from scratch:

  • examples/connections/heroku-postgres.json
  • examples/connections/redshift.json
  • examples/connections/sharepoint-unstructured.json
  • examples/connections/snowflake-connection.json
  • examples/connections/ingest-api-connection.json
  • examples/connections/ingest-api-schema.json

Typical Ingestion API setup flow:

sf data360 connection create -o <org> -f examples/connections/ingest-api-connection.json 2>/dev/null
sf data360 connection schema-upsert -o <org> --name <connector-id> -f examples/connections/ingest-api-schema.json 2>/dev/null
sf data360 connection schema-get -o <org> --name <connector-id> 2>/dev/null

7. Discover payload fields for unknown connector types

Create one in the UI, then inspect it directly:

sf api request rest "/services/data/v66.0/ssot/connections/<id>" -o <org>

High-Signal Gotchas

  • connection list has no true global "list all" mode; query by connector type.
  • The connector catalog name and connection connector type are not always the same label.
  • connection test may need --connector-type for name resolution when the source is not a default Salesforce connector.
  • An empty connection list usually means "enabled but not configured yet", not "feature disabled".
  • Heroku Postgres, Redshift, Snowflake, SharePoint Unstructured, and Ingestion API all use different credential and parameter shapes; reuse the curated examples instead of guessing.
  • SharePoint Unstructured uses clientId, clientSecret, and tokenEndpoint in the credentials array and does not require a parameters array.
  • Snowflake uses key-pair auth and can often be created through the API, but downstream stream creation can still remain UI-only.
  • Ingestion API connector setup is incomplete until connection schema-upsert has uploaded the object schema.
  • Some external connector credential setup still depends on UI-side configuration or external-system permissions.

Output Format

Connect task: <inspect / create / test / update>
Connector type: <SalesforceDotCom / REDSHIFT / SNOWFLAKE / SPUnstructuredDocument / IngestApi / ...>
Target org: <alias>
Commands: <key commands run>
Verification: <passed / partial / blocked>
Next step: <prepare phase or connector follow-up>

References

用于 Salesforce Data Cloud 的 Harmonize 阶段,处理 DMO、字段映射、关系、身份解析、统一画像及数据图谱。适用于模式协调与统一工作,排除数据准备、细分或查询任务。
用户需要创建或管理 DMO(数据模型对象) 配置字段映射和实体关系 执行身份解析(Identity Resolution) 构建统一画像或数据图谱 查找通用 ID
skills/data360-harmonize/SKILL.md
npx skills add forcedotcom/sf-skills --skill data360-harmonize -g -y
SKILL.md
Frontmatter
{
    "name": "data360-harmonize",
    "metadata": {
        "version": "1.0"
    },
    "description": "Salesforce Data Cloud Harmonize phase. Use this skill when the user works with DMOs, mappings, relationships, identity resolution, unified profiles, data graphs, or universal IDs. TRIGGER when: user works with DMOs, mappings, relationships, identity resolution, unified profiles, data graphs, or universal IDs. DO NOT TRIGGER when: the task is only about streams\/DLOs (use data360-prepare), segments\/insights (use data360-segment), retrieval\/search (use data360-query), or STDM\/session tracing (use agentforce-observe).",
    "compatibility": "Requires an external community sf data360 CLI plugin and a Data Cloud-enabled org"
}

data360-harmonize: Data Cloud Harmonize Phase

Use this skill when the user needs schema harmonization and unification work: DMOs, field mappings, relationships, identity resolution, unified profiles, data graphs, or universal ID lookup.

When This Skill Owns the Task

Use data360-harmonize when the work involves:

  • sf data360 dmo *
  • sf data360 identity-resolution *
  • sf data360 data-graph *
  • sf data360 profile *
  • sf data360 universal-id lookup

Delegate elsewhere when the user is:


Required Context to Gather First

Ask for or infer:

  • source DLO and target DMO names
  • whether the task is schema creation, mapping, IR, or graph-related
  • target org alias
  • whether a ruleset already exists
  • the user’s desired unified entity model

Core Operating Rules

  • Inspect DMO schema before creating mappings.
  • Run the shared readiness classifier before mutating harmonization assets: node ../data360-orchestrate/scripts/diagnose-org.mjs -o <org> --phase harmonize --json.
  • Prefer dmo list --all when browsing the catalog, but use first-page dmo list for fast readiness checks.
  • Use query describe or dmo get --json instead of inventing unsupported describe flows.
  • Treat identity resolution runs as asynchronous and verify results after execution.
  • Keep unified-profile work separate from STDM/session tracing work.

Recommended Workflow

1. Classify readiness for harmonize work

node ../data360-orchestrate/scripts/diagnose-org.mjs -o <org> --phase harmonize --json

2. Inspect the catalog

sf data360 dmo list --all -o <org> 2>/dev/null
sf data360 identity-resolution list -o <org> 2>/dev/null

3. Inspect schema before mapping

sf data360 query describe -o <org> --table ssot__Individual__dlm 2>/dev/null
sf data360 dmo get -o <org> --name ssot__Individual__dlm --json 2>/dev/null

4. Create or review mappings intentionally

sf data360 dmo mapping-list -o <org> --source Contact_Home__dll --target ssot__Individual__dlm 2>/dev/null
sf data360 dmo map-to-canonical -o <org> --dlo Contact_Home__dll --dmo ssot__Individual__dlm --dry-run 2>/dev/null

5. Run IR only after mappings are trustworthy

sf data360 identity-resolution create -o <org> -f ir-ruleset.json 2>/dev/null
sf data360 identity-resolution run -o <org> --name Main 2>/dev/null

High-Signal Gotchas

  • dmo list should usually use --all.
  • Use query describe or dmo get --json; there is no dmo describe command.
  • Mapping and related commands can be sensitive to API-version differences.
  • Unified DMO names are ruleset-specific rather than generic.
  • Data graph definitions are sensitive to field selection and relationship shape.
  • If dmo list works but identity-resolution list is gated, treat that as a phase-specific gap rather than a full Data Cloud outage.

Output Format

Harmonize task: <dmo / mapping / relationship / ir / data-graph>
Source/target: <dlo → dmo or ruleset/graph names>
Target org: <alias>
Artifacts: <json files / commands>
Verification: <passed / partial / blocked>
Next step: <segment / retrieve / follow-up>

References

Salesforce Data Cloud 多阶段工作流编排技能。适用于跨连接、准备、调和、细分及激活阶段的管道设计、故障排查、数据空间与套件管理。若任务局限于单阶段或涉及STDM/标准SOQL,则委托给专用技能。
需要多步骤Data Cloud管道设置 跨阶段故障排查 管理数据空间或数据套件 决策数据从摄入到激活的流转路径
skills/data360-orchestrate/SKILL.md
npx skills add forcedotcom/sf-skills --skill data360-orchestrate -g -y
SKILL.md
Frontmatter
{
    "name": "data360-orchestrate",
    "metadata": {
        "version": "1.0"
    },
    "description": "Salesforce Data Cloud product orchestrator for connect→prepare→harmonize→segment→act workflows. Use this skill when the user needs a multi-step Data Cloud pipeline, cross-phase troubleshooting, or data space and data kit management. TRIGGER when: user needs a multi-step Data Cloud pipeline, asks to set up or troubleshoot Data Cloud across phases, manages data spaces or data kits, or wants a cross-phase sf data360 workflow. DO NOT TRIGGER when: work is isolated to a single phase (use the matching phase-specific skill), the task is STDM\/session tracing\/parquet telemetry (use agentforce-observe), standard CRM SOQL (use platform-soql-query), or Apex implementation (use platform-apex-generate).",
    "compatibility": "Requires an external community sf data360 CLI plugin and a Data Cloud-enabled org"
}

data360-orchestrate: Salesforce Data Cloud Orchestrator

Use this skill when the user needs product-level Data Cloud workflow guidance rather than a single isolated command family: pipeline setup, cross-phase troubleshooting, data spaces, data kits, or deciding whether a task belongs in Connect, Prepare, Harmonize, Segment, Act, or Retrieve.

This skill intentionally follows sf-skills house style while using the external sf data360 command surface as the runtime. The plugin is not vendored into this repo.


When This Skill Owns the Task

Use data360-orchestrate when the work involves:

  • multi-phase Data Cloud setup or remediation
  • data spaces (sf data360 data-space *)
  • data kits (sf data360 data-kit *)
  • health checks (sf data360 doctor)
  • CRM-to-unified-profile pipeline design
  • deciding how to move from ingestion → harmonization → segmentation → activation
  • cross-phase troubleshooting where the root cause is not yet clear

Delegate to a phase-specific skill when the user is focused on one area:

Phase Use this skill Typical scope
Connect data360-connect connections, connectors, source discovery
Prepare data360-prepare data streams, DLOs, transforms, DocAI
Harmonize data360-harmonize DMOs, mappings, identity resolution, data graphs
Segment data360-segment segments, calculated insights
Act data360-activate activations, activation targets, data actions
Retrieve data360-query SQL, search indexes, vector search, async query

Delegate outside the family when the user is:


Required Context to Gather First

Ask for or infer:

  • target org alias
  • whether the plugin is already installed and linked
  • whether the user wants design guidance, read-only inspection, or live mutation
  • data sources involved: CRM objects, external databases, file ingestion, knowledge, etc.
  • desired outcome: unified profiles, segments, activations, vector search, analytics, or troubleshooting
  • whether the user is working in the default data space or a custom one
  • whether the org has already been classified with scripts/diagnose-org.mjs
  • which command family is failing today, if any

If plugin availability or org readiness is uncertain, start with:


Core Operating Rules

  • Use the external sf data360 plugin runtime; do not reimplement or vendor the command layer.
  • Prefer the smallest phase-specific skill once the task is localized.
  • Run readiness classification before mutation-heavy work. Prefer scripts/diagnose-org.mjs over guessing from one failing command.
  • For sf data360 commands, suppress linked-plugin warning noise with 2>/dev/null unless the stderr output is needed for debugging.
  • Distinguish Data Cloud SQL from CRM SOQL.
  • Do not treat sf data360 doctor as a full-product readiness check; the current upstream command only checks the search-index surface.
  • Do not treat query describe as a universal tenant probe; only use it with a known DMO/DLO table after broader readiness is confirmed.
  • Preserve Data Cloud-specific API-version workarounds when they matter.
  • Prefer generic, reusable JSON definition files over org-specific workshop payloads.

Recommended Workflow

1. Verify the runtime and auth

Confirm:

  • sf is installed
  • the community Data Cloud plugin is linked
  • the target org is authenticated

Recommended checks:

sf data360 man
sf org display -o <alias>
bash ./scripts/verify-plugin.sh <alias>

Treat sf data360 doctor as a broad health signal, not the sole gate. On partially provisioned orgs it can fail even when read-only command families like connectors, DMOs, or segments still work.

2. Classify readiness before changing anything

Run the shared classifier first:

node ./scripts/diagnose-org.mjs -o <org> --json

Only use a query-plane probe after you know the table name is real:

node ./scripts/diagnose-org.mjs -o <org> --phase retrieve --describe-table MyDMO__dlm --json

Use the classifier to distinguish:

  • empty-but-enabled modules
  • feature-gated modules
  • query-plane issues
  • runtime/auth failures

3. Discover existing state with read-only commands

Use targeted inspection after classification:

sf data360 doctor -o <org> 2>/dev/null
sf data360 data-space list -o <org> 2>/dev/null
sf data360 data-stream list -o <org> 2>/dev/null
sf data360 dmo list -o <org> 2>/dev/null
sf data360 identity-resolution list -o <org> 2>/dev/null
sf data360 segment list -o <org> 2>/dev/null
sf data360 activation platforms -o <org> 2>/dev/null

4. Localize the phase

Route the task:

  • source/connector issue → Connect
  • ingestion/DLO/stream issue → Prepare
  • mapping/IR/unified profile issue → Harmonize
  • audience or insight issue → Segment
  • downstream push issue → Act
  • SQL/search/index issue → Retrieve

5. Choose deterministic artifacts when possible

Prefer JSON definition files and repeatable scripts over one-off manual steps. Generic templates live in:

  • assets/definitions/data-stream.template.json
  • assets/definitions/dmo.template.json
  • assets/definitions/mapping.template.json
  • assets/definitions/relationship.template.json
  • assets/definitions/identity-resolution.template.json
  • assets/definitions/data-graph.template.json
  • assets/definitions/calculated-insight.template.json
  • assets/definitions/segment.template.json
  • assets/definitions/activation-target.template.json
  • assets/definitions/activation.template.json
  • assets/definitions/data-action-target.template.json
  • assets/definitions/data-action.template.json
  • assets/definitions/search-index.template.json

6. Verify after each phase

Typical verification:

  • stream/DLO exists
  • DMO/mapping exists
  • identity resolution run completed
  • unified records or segment counts look correct
  • activation/search index status is healthy

High-Signal Gotchas

  • connection list requires --connector-type.
  • dmo list --all is useful when you need the full catalog, but first-page dmo list is often enough for readiness checks and much faster.
  • Segment creation may need --api-version 64.0.
  • segment members returns opaque IDs; use SQL joins for human-readable details.
  • sf data360 doctor can fail on partially provisioned orgs even when some read-only commands still work; fall back to targeted smoke checks.
  • query describe errors such as Couldn't find CDP tenant ID or DataModelEntity ... not found are query-plane clues, not automatic proof that the whole product is disabled.
  • Many long-running jobs are asynchronous in practice even when the command returns quickly.
  • Some Data Cloud operations still require UI setup outside the CLI runtime.

Output Format

When finishing, report in this order:

  1. Task classification
  2. Runtime status
  3. Readiness classification
  4. Phase(s) involved
  5. Commands or artifacts used
  6. Verification result
  7. Next recommended step

Suggested shape:

Data Cloud task: <setup / inspect / troubleshoot / migrate>
Runtime: <plugin ready / missing / partially verified>
Readiness: <ready / ready_empty / partial / feature_gated / blocked>
Phases: <connect / prepare / harmonize / segment / act / retrieve>
Artifacts: <json files, commands, scripts>
Verification: <passed / partial / blocked>
Next step: <next phase, setup guidance, or cross-skill handoff>

Cross-Skill Integration

Need Delegate to Reason
load or clean CRM source data platform-data-manage seed or fix source records before ingestion
create missing CRM schema platform-custom-object-generate, platform-custom-field-generate Data Cloud expects existing objects/fields
deploy permissions or bundles platform-metadata-deploy environment preparation
write Apex against Data Cloud outputs platform-apex-generate code implementation
Flow automation after segmentation/activation automation-flow-generate declarative orchestration
session tracing / STDM / parquet analysis agentforce-observe different Data Cloud use case

Reference Map

Start here

Phase skills

Deterministic helpers

用于 Salesforce Data Cloud 的检索阶段,支持同步/异步 SQL、向量搜索、混合搜索及元数据检查。适用于查询和数据探索,排除标准 SOQL、Segment 设计及遥测分析。
用户运行 Data Cloud SQL 查询 执行异步查询任务 进行向量或混合搜索 操作搜索索引工作流 检查 Data Cloud 对象元数据
skills/data360-query/SKILL.md
npx skills add forcedotcom/sf-skills --skill data360-query -g -y
SKILL.md
Frontmatter
{
    "name": "data360-query",
    "metadata": {
        "version": "1.0"
    },
    "description": "Salesforce Data Cloud Retrieve phase. Use this skill when the user runs Data Cloud SQL, async queries, vector search, search-index workflows, or metadata introspection for Data Cloud objects. TRIGGER when: user runs Data Cloud SQL, describe, async queries, vector search, search-index workflows, or metadata introspection for Data Cloud objects. DO NOT TRIGGER when: the task is standard CRM SOQL (use platform-soql-query), segment creation or calculated insight design (use data360-segment), or STDM\/session tracing\/parquet analysis (use agentforce-observe).",
    "compatibility": "Requires an external community sf data360 CLI plugin and a Data Cloud-enabled org"
}

data360-query: Data Cloud Retrieve Phase

Use this skill when the user needs query, search, and metadata introspection for Data Cloud: sync SQL, paginated SQL, async query workflows, table describe, vector search, hybrid search, or search index operations.

When This Skill Owns the Task

Use data360-query when the work involves:

  • sf data360 query *
  • sf data360 search-index *
  • sf data360 metadata *
  • sf data360 profile * or sf data360 insight * inspection
  • understanding Data Cloud SQL results or query shape

Delegate elsewhere when the user is:


Required Context to Gather First

Ask for or infer:

  • target org alias
  • whether the user needs quick count, medium result set, large export, schema inspection, or semantic search
  • table/index name if known
  • whether the task is read-only SQL or search-index lifecycle management

Core Operating Rules

  • Treat Data Cloud SQL as its own query language, not SOQL.
  • Run the shared readiness classifier before relying on query/search surfaces: node ../data360-orchestrate/scripts/diagnose-org.mjs -o <org> --phase retrieve --json.
  • Use describe before guessing columns.
  • Prefer sqlv2 or async query flows for larger result sets.
  • Use vector search or hybrid search only when the search index lifecycle is healthy.
  • Keep STDM/parquet/session-tracing workflows out of this skill family.

Recommended Workflow

1. Classify readiness for retrieve work

node ../data360-orchestrate/scripts/diagnose-org.mjs -o <org> --phase retrieve --json
# optional query-plane probe, only with a real table name
node ../data360-orchestrate/scripts/diagnose-org.mjs -o <org> --phase retrieve --describe-table MyDMO__dlm --json

2. Choose the smallest correct query shape

sf data360 query sql -o <org> --sql 'SELECT COUNT(*) FROM "ssot__Individual__dlm"' 2>/dev/null
sf data360 query sqlv2 -o <org> --sql 'SELECT * FROM "ssot__Individual__dlm"' 2>/dev/null
sf data360 query async-create -o <org> --sql 'SELECT * FROM "ssot__Individual__dlm"' 2>/dev/null

3. Use describe before guessing fields

sf data360 query describe -o <org> --table ssot__Individual__dlm 2>/dev/null

4. Use vector or hybrid search only when an index exists

sf data360 search-index list -o <org> 2>/dev/null
sf data360 query vector -o <org> --index Knowledge_Index --query "reset password" --limit 5 2>/dev/null
sf data360 query hybrid -o <org> --index Knowledge_Index --query "reset password" --limit 5 2>/dev/null
sf data360 query hybrid -o <org> --index Insurance_Index --query "weather damage coverage" --prefilter "Type_of_Insurance__c='Home'" --limit 10 2>/dev/null

5. Reuse curated search-index examples when creating indexes

Use the phase-owned examples instead of inventing JSON from scratch:

  • examples/search-indexes/vector-knowledge.json
  • examples/search-indexes/hybrid-structured.json

High-Signal Gotchas

  • Data Cloud SQL is not SOQL.
  • Table names should be double-quoted in SQL.
  • sqlv2 is better than ad hoc OFFSET paging for medium result sets.
  • async query is preferable for large results.
  • search-index operations and vector/hybrid queries depend on the index lifecycle being healthy.
  • Hybrid search can use --prefilter, but only on fields configured as prefilter-capable when the search index was created.
  • HNSW index parameters are typically read-only on create; leave userValues: [] unless the platform explicitly documents otherwise.
  • query describe is not a universal tenant probe; only run it with a known DMO or DLO table after broader readiness has been confirmed.

Output Format

Retrieve task: <sql / sqlv2 / async / describe / vector / search-index>
Target org: <alias>
Target object: <table or index>
Commands: <key commands run>
Verification: <query rows / schema / status>
Next step: <segment / harmonize / follow-up>

References

用于Salesforce Data Cloud的受众细分与洞察管理。涵盖创建、发布Segment及计算洞察,检查成员数,排查SQL问题。不处理数据映射、激活或只读查询等任务。
创建或发布Data Cloud Segment 管理计算洞察(Calculated Insights) 检查Segment成员数量 排查受众SQL问题
skills/data360-segment/SKILL.md
npx skills add forcedotcom/sf-skills --skill data360-segment -g -y
SKILL.md
Frontmatter
{
    "name": "data360-segment",
    "metadata": {
        "version": "1.0"
    },
    "description": "Salesforce Data Cloud Segment phase. Use this skill when the user creates or publishes segments, manages calculated insights, or troubleshoots audience SQL in Data Cloud. TRIGGER when: user creates or publishes segments, manages calculated insights, inspects segment counts or membership, or troubleshoots audience SQL in Data Cloud. DO NOT TRIGGER when: the task is DMO\/mapping\/identity-resolution work (use data360-harmonize), activation work (use data360-activate), query\/search-index work (use data360-query), or Standard Data Model (STDM)\/session tracing (use agentforce-observe).",
    "compatibility": "Requires an external community sf data360 CLI plugin and a Data Cloud-enabled org"
}

data360-segment: Data Cloud Segment Phase

Use this skill when the user needs audience and insight work: segments, calculated insights, publish workflows, member counts, or troubleshooting Data Cloud segment SQL.

When This Skill Owns the Task

Use data360-segment when the work involves:

  • sf data360 segment *
  • sf data360 calculated-insight *
  • segment publish workflows
  • member counts and segment troubleshooting
  • calculated insight execution and verification

Delegate elsewhere when the user is:


Required Context to Gather First

Ask for or infer:

  • target org alias
  • unified DMO (Data Model Object) or base entity name
  • whether the user wants create, publish, inspect, or troubleshoot
  • whether the asset is a segment or calculated insight
  • expected success metric: member count, aggregate value, or publish status

Core Operating Rules

  • Treat Data Cloud segment SQL as distinct from CRM SOQL.
  • Run the shared readiness classifier from the data360-orchestrate skill before mutating audience assets: node ../data360-orchestrate/scripts/diagnose-org.mjs -o <org> --phase segment --json.
  • Prefer reusable JSON definitions for repeatable segment and CI creation.
  • Use --api-version 64.0 when segment creation behavior is unstable on newer defaults.
  • Verify with counts or SQL after publish/run steps instead of assuming success.
  • Use SQL joins rather than segment members when readable member details are needed.

Recommended Workflow

1. Classify readiness for segment work

node ../data360-orchestrate/scripts/diagnose-org.mjs -o <org> --phase segment --json

2. Inspect current state

sf data360 segment list -o <org> 2>/dev/null
sf data360 calculated-insight list -o <org> 2>/dev/null

3. Create with reusable JSON definitions

sf data360 segment create -o <org> -f segment.json --api-version 64.0 2>/dev/null
sf data360 calculated-insight create -o <org> -f ci.json 2>/dev/null

4. Publish or run explicitly

sf data360 segment publish -o <org> --name My_Segment 2>/dev/null
sf data360 calculated-insight run -o <org> --name Lifetime_Value 2>/dev/null

5. Verify with counts or SQL

sf data360 segment count -o <org> --name My_Segment 2>/dev/null
sf data360 query sql -o <org> --sql 'SELECT COUNT(*) FROM "UnifiedssotIndividualMain__dlm"' 2>/dev/null

High-Signal Gotchas

  • Segment creation can require --api-version 64.0.
  • segment members returns opaque IDs; use SQL joins when human-readable member details are needed.
  • Segment SQL is not SOQL.
  • Calculated insight assets and segment SQL have different limitations.
  • Publish/run steps may kick off asynchronous work even when the command returns quickly.
  • An empty segment or calculated-insight list usually means the module is reachable but unconfigured, not unavailable.

Output Format

Segment task: <segment / calculated-insight>
Action: <create / publish / inspect / troubleshoot>
Target org: <alias>
Artifacts: <definition files / commands>
Verification: <member count / query result / publish state>
Next step: <act / retrieve / follow-up>

References

审计 Lightning Web Components 的 SLDS 合规性,结合 linter 与静态分析生成评分质量报告。用于评估组件质量、生产就绪度及合规等级,不用于修复或构建新组件。
score my component SLDS scorecard quality report audit SLDS compliance how good is my SLDS check component quality rate my component evaluate my component is this component ready to ship? look at my LWC for issues audit this before I submit review my component before code review
skills/design-systems-slds-validate/SKILL.md
npx skills add forcedotcom/sf-skills --skill design-systems-slds-validate -g -y
SKILL.md
Frontmatter
{
    "name": "design-systems-slds-validate",
    "metadata": {
        "version": "1.0"
    },
    "description": "Audit Lightning Web Components for SLDS compliance and produce a scored quality report. Runs the SLDS linter, analyzes CSS for theming hook usage and pairing, checks HTML for accessibility attributes, and scores findings across categories into an overall grade. Use when asked to \"score my component\", \"SLDS scorecard\", \"quality report\", \"audit SLDS compliance\", \"how good is my SLDS\", \"check component quality\", \"rate my component\", \"evaluate my component\", \"is this component ready to ship?\", \"look at my LWC for issues\", \"audit this before I submit\", \"review my component before code review\", or any time a user wants a quality assessment or production-readiness check on an LWC or SLDS component. Not for fixing violations (use design-systems-slds2-migrate) or building new components (use design-systems-slds-apply)."
}

SLDS Quality Audit

Audit Lightning Web Components for SLDS compliance and produce an automated scorecard plus a required manual review gate. Combines SLDS linter output with supplementary static analysis to catch what the linter misses.

Scope

Also valid for: auditing SLDS compliance across a project or component set, and before/after quality comparison after making changes.

Not for:

  • Fixing linter violations — use design-systems-slds2-migrate instead
  • Building new components — use design-systems-slds-apply instead
  • Just running the linter — run npx @salesforce-ux/slds-linter@latest lint . directly
  • Full WCAG accessibility audit — this skill checks attribute presence only (labels, alt text, focus indicators), not contrast ratios, keyboard flows, or screen reader behavior
  • Framework-specific template auditing beyond .css, .html, and .js files — JSX/TSX/Vue/Svelte outputs need additional manual review

Quality Validation Process

1. Run SLDS Linter     → Collect violation counts (linter's job)
2. Run Analyze Script  → Check what linter doesn't cover (supplementary)
3. Agent Review        → Required manual review gate
4. Score & Grade       → Compute automated score + final recommendation
5. Generate Report     → Produce formatted scorecard

Step 1: Run SLDS Linter

Run the linter to collect baseline violation data:

npx @salesforce-ux/slds-linter@latest lint <component-path> 2>&1

Count violations by rule. These feed directly into the Linter Compliance score:

Rule Impact
slds/class-override Breaks theming, dark mode
slds/lwc-token-to-slds-hook SLDS 1 technical debt
slds/no-hardcoded-values Breaks theming, accessibility

Linter Compliance Score = 100 - (total_violations × 10), minimum 0.

If the linter is unavailable (no Node.js, no network access, CI sandbox restrictions): skip this step, note "Linter not run" in the report header, mark Linter Compliance as N/A, and compute the Overall score using the remaining 4 categories renormalized to 100%:

Overall (linter unavailable) = (Theming × 0.29) + (Accessibility × 0.29)
                              + (CodeQuality × 0.21) + (ComponentUsage × 0.21)

Step 2: Run Supplementary Analysis

Run the analyze script to catch issues the linter doesn't cover. The bundled analyzer scans .css, .html, and .js files only:

node scripts/analyze-quality.cjs <component-path>

The script outputs JSON with findings organized by severity. It checks:

CSS Checks (linter-complementary)

Check What It Catches Severity
Missing fallbacks var(--slds-g-*) without a fallback value Critical
Invented hooks (T051) --slds-g-* tokens not found in hooks-index.json (requires --hooks-index) Critical
Hook pairing Background hooks without matching foreground hooks Warning
!important Specificity overrides Warning
Magic pixel values Hardcoded px not using spacing hooks Warning
High z-index z-index values > 99 Warning
Outline removal outline: none without alternative focus style Warning

JS Checks

Check What It Catches Severity
Inline style assignment .style.*= direct property assignment Warning
SLDS class manipulation Dynamic .classList.add('slds-*') manipulation Warning

HTML Checks

Check What It Catches Severity
LBC input labels <lightning-input> without label attribute Critical
Icon alt text <lightning-icon> without alternative-text Critical
Image alt text <img> without alt Critical
Heading hierarchy Skipped heading levels (h2 to h4) Warning
Positive tabindex tabindex values other than 0 or -1 Warning
Clickable divs <div onclick> instead of <button> Warning
Inline styles style="..." attributes Warning
Native elements <input>, <button>, <select> where LBC alternatives exist Warning

Hook Pairing Validation

The script checks that background/foreground hooks are semantically paired:

surface-* backgrounds     → on-surface-* text
surface-container-* bg    → on-surface-* text
accent-* backgrounds      → on-accent-* text
accent-container-* bg     → on-accent-* text

Limitation: Hook pairing is checked at the file level, not per-selector. A file with surface-1 in .classA and on-accent-1 in .classB would pass because both surface and accent families are present. Review pairing correctness per-selector during manual review (Step 3).

Invented Hook Detection (T051)

The script cross-references every --slds-g-* token in CSS against hooks-index.json. Any hook not found in metadata is flagged as critical — this catches the most common agent mistake of inventing hooks from naming patterns.

Step 3: Agent Manual Review

These checks require understanding the component's purpose and cannot be automated reliably. Review each and classify findings as either:

  • Blocking — incorrect blueprint structure, missing required states, or semantic/interaction issues that make the component not production-ready
  • Advisory — worthwhile improvements that do not block shipping on their own
Review Area What to Look For
Loading states Does the component show a spinner or skeleton when fetching data?
Error states Are errors surfaced to the user with actionable messages?
Empty states Is there a meaningful empty state when no data exists?
Disabled states Do interactive elements visually and functionally handle disabled?
Semantic HTML Are <nav>, <article>, <section> used where appropriate?
SLDS blueprint compliance Do cards, modals, forms follow SLDS blueprint structure?

Manual review findings are not automated, but they do affect the final recommendation. Do not report an automated grade as the only verdict.

Step 4: Calculate Automated Scores and Final Recommendation

Component Complexity

Before scoring, classify the component to give the score context:

Complexity Criteria Report Note
Small 1-2 files, < 100 total lines Score is high-confidence (small surface area)
Medium 3-6 files, 100-500 total lines Score reflects typical component
Large 7+ files, 500+ total lines Score reflects absolute issue count — even well-built large components may score lower

Include the complexity classification in the report header. This prevents misreading a "B" on a 1000-line component vs. a "B" on a 20-line component.

Automated Scoring Formula

Category Score = 100 - (critical_issues × 10) - (warnings × 3) - (info × 1)
Minimum score: 0

Categories and Weights

Category Weight Source
Linter Compliance 30% SLDS linter output (Step 1)
Theming 20% Script: fallbacks, hook pairing (Step 2)
Accessibility 20% Script: labels, alt text, focus (Step 2)
Code Quality 15% Script: !important, inline styles, z-index (Step 2)
Component Usage 15% Script: native elements (Step 2) plus manual semantic/blueprint review (Step 3)

Automated Overall Score

Overall = (Linter × 0.30) + (Theming × 0.20) + (Accessibility × 0.20)
        + (CodeQuality × 0.15) + (ComponentUsage × 0.15)

Automated Grade Thresholds

Score Grade Meaning
90-100 A Excellent automated score
80-89 B Good automated score
70-79 C Acceptable automated score
60-69 D Weak automated score
0-59 F Failing automated score

Manual Review Gate

After computing the automated score, apply the manual review outcome:

Gate When to use it Effect on final recommendation
Pass No manual findings Final recommendation can follow the automated score
Advisory Only non-blocking manual findings Final recommendation can be "Ready with follow-ups" at best
Blocking One or more blocking manual findings Final recommendation is not ready for production, regardless of automated grade

Final Recommendation Rules

Use both the automated score and the manual review gate:

Final Recommendation Conditions
Ready for production Automated grade A/B, no critical findings, manual gate = Pass
Ready with follow-ups Automated grade A/B, no critical findings, manual gate = Advisory
Needs work Any critical findings, automated grade C/D, or manual gate = Blocking
Failing Automated grade F

Step 5: Generate Quality Report

Use the template in report-format.md to produce the final report. Default to the compact format for initial output and expand sections on request.

The report includes:

  • Executive summary with automated grade and final recommendation
  • Manual review gate outcome (Pass, Advisory, or Blocking)
  • Scores by category with visual indicators
  • Detailed findings organized by severity
  • Specific code locations and recommendations
  • Checklist of required actions

Quick Validation Mode

For a rapid quality check without full analysis:

  1. Run linter: npx @salesforce-ux/slds-linter@latest lint <path>
  2. Count violations by type
  3. Report summary only
Quick Quality Check: <component-name>
─────────────────────────────────────
Linter Violations:
  • Class Override:     0
  • Deprecated Tokens:  3
  • Hardcoded Values:   5

Quick Automated Grade: C (estimated)
Run full validation for detailed report.

Edge Cases and False Positives

Situation Guidance
Headless components (JS-only, no HTML) Skip HTML checks; score only CSS + linter categories
Wrapper/container components May legitimately have minimal CSS; don't penalize low hook usage
Intentional native elements <button> inside custom SLDS blueprints is correct; suppress C002 if inside an slds-* blueprint structure
Components outside LEX LWR/Experience Cloud components may not use Lightning Base Components; note context in report
Test/demo components Lower the bar — note in report but don't block on warnings

If a check produces a false positive, note it in the report as "suppressed" with justification rather than silently dropping it.


References

  • Quality Checks - Complete list of all quality checks with detection patterns
  • Report Format - Quality report template and formatting guide
  • Analyze Script - Automated analysis for linter-complementary checks
  • design-systems-slds2-migrate skill - How to fix linter violations
  • design-systems-slds-apply skill - Guide for building new components with correct patterns
指导将 Lightning Web Components 从 SLDS 1 迁移至 SLDS 2。通过运行 linter 自动修复并手动替换硬编码 CSS 值、过时 Token 及类覆盖,涵盖颜色、间距等六类样式钩子,确保组件符合 SLDS 2 规范。
SLDS 2 迁移 SLDS uplift linter violations LWC token migration class overrides hardcoded CSS values replacement styling hook selection no-hardcoded-values no-slds-class-overrides lwc-to-slds-hooks no-deprecated-tokens-slds1 SLDS component migration
skills/design-systems-slds2-migrate/SKILL.md
npx skills add forcedotcom/sf-skills --skill design-systems-slds2-migrate -g -y
SKILL.md
Frontmatter
{
    "name": "design-systems-slds2-migrate",
    "metadata": {
        "version": "1.0"
    },
    "description": "Migrate Lightning Web Components from SLDS 1 to SLDS 2 by running the SLDS linter and fixing violations. Use this skill whenever users mention SLDS 2, SLDS uplift, linter violations, LWC token migration, class overrides, hardcoded CSS values that need SLDS hook replacement, or styling hook selection. Covers all styling hook categories — color, spacing, sizing, typography, borders, radius, and shadows. Also use when users mention no-hardcoded-values, no-slds-class-overrides, lwc-to-slds-hooks, no-deprecated-tokens-slds1, or ask about SLDS component migration — even if they don't explicitly say \"uplift\" or \"migration\"."
}

Goal

Systematically migrate Lightning Web Components from SLDS 1 to SLDS 2 using the SLDS linter and structured guidance for fixing violations across all styling hook categories.

SLDS 2 Styling Hook Categories

Category Hook Prefix What It Replaces
Color --slds-g-color-* Hardcoded colors, --lwc-color* tokens
Spacing --slds-g-spacing-* Hardcoded margins, padding, gaps
Sizing --slds-g-sizing-* Hardcoded widths, heights, dimensions
Typography --slds-g-font-* Hardcoded font sizes, weights, line heights
Border/Radius --slds-g-radius-border-*, --slds-g-sizing-border-* Hardcoded border-radius, border-width
Shadow --slds-g-shadow-* Hardcoded box-shadow values

Color hooks require the most judgment (context-dependent selection). Non-color hooks are mostly numbered scales with straightforward mappings.

Prerequisites

  • Node.js 14.x or higher installed
  • Access to component CSS and markup files (.html for LWC, .cmp for Aura)
  • Terminal/command line access to run linter
  • Git repository for backup (recommended)

Workflow

1. **REQUIRED — ALWAYS run first:** npx @salesforce-ux/slds-linter@latest lint --fix . — NEVER skip this step. This handles simple violations automatically.
2. Review linter output -> Identify remaining manual fixes needed
3. Fix by violation type -> Use per-rule reference guides
4. Choose the right hook -> Context-first, inspect HTML before deciding
5. Validate -> Re-run linter and confirm zero errors

Step 1: Run SLDS Linter

MANDATORY: This step is NOT optional.

npx @salesforce-ux/slds-linter@latest lint --fix .

The linter analyzes all CSS and markup files (.html for LWC, .cmp for Aura), auto-fixes simple violations, and reports remaining issues requiring manual intervention.

Step 2: Analyze Linter Output

The linter reports violations in this format:

componentName.css
  15:3  warning  Overriding slds-button isn't supported. To differentiate SLDS and
                 custom classes, create a CSS class in your namespace.
                 Examples: myapp-input, myapp-button.                        slds/no-slds-class-overrides

  23:5  error    The '--lwc-colorBackground' design token is deprecated. Replace it with
                 the SLDS 2 styling hook and set the fallback to '--lwc-colorBackground'.
                 1. --slds-g-color-surface-2
                 2. --slds-g-color-surface-container-2                      slds/lwc-token-to-slds-hook

  30:8  warning  Consider replacing the #ffffff static value with an SLDS 2 styling hook
                 that has a similar value:
                 1. --slds-g-color-surface-1
                 2. --slds-g-color-surface-container-1
                 3. --slds-g-color-on-accent-1
                 4. --slds-g-color-on-accent-2
                 5. --slds-g-color-on-accent-3                              slds/no-hardcoded-values-slds2

  31:15  error   Consider removing t(fontSizeMedium) or replacing it with
                 var(--slds-g-font-size-base, var(--lwc-fontSizeMedium, 0.8125rem)).
                 Set the fallback to t(fontSizeMedium). For more info, see
                 Styling Hooks on lightningdesignsystem.com.               slds/no-deprecated-tokens-slds1

Four violation types, each with its own fix approach (see Step 3).

Important: The linter flags all hardcoded values. Fix color, spacing, sizing, typography, border, and shadow values — but skip layout values (100%, auto, 0, inherit, none). See rule-no-hardcoded-values.md for the full fix-vs-skip triage table.

Step 3: Fix Violations by Type

Each rule has a dedicated reference guide with full examples and decision logic:

Violation Rule Quick Summary Reference
slds/no-hardcoded-values-slds2 Replace hardcoded values with SLDS hook + original as fallback rule-no-hardcoded-values.md
slds/lwc-token-to-slds-hook Replace --lwc-* tokens with SLDS 2 hook, keep LWC token as fallback rule-lwc-token-to-slds-hook.md
slds/no-slds-class-overrides Create component-prefixed class, add to markup alongside SLDS class rule-no-slds-class-overrides.md
slds/no-deprecated-tokens-slds1 Replace legacy t()/token() syntax with SLDS 2 hook + LWC fallback rule-no-deprecated-tokens-slds1.md

Always include fallback valuesvar(--slds-g-hook, originalValue) where originalValue is the exact original from the source CSS.

Class Override Quick Reference

Class overrides require changes to both CSS and markup (.html or .cmp). This is the most commonly missed step:

  1. CSS: Rename .slds-* selector → {componentName}-{sldsElementPart} (camelCase)
  2. Markup: Add the new class alongside the SLDS class — never remove the SLDS class
/* Before */ .slds-button { border-radius: 8px; }
/* After */  .myComponent-button { border-radius: 8px; }
<!-- Markup: both classes --> <button class="slds-button myComponent-button">Click</button>

See rule-no-slds-class-overrides.md for descendant selectors, multi-class selectors, and naming conventions.

Step 4: Choose the Right Hook

Color hooks require context-based selection. REQUIRED: When any violation involves a color property (color, background-color, background, fill, border-color), you MUST read color-hooks-decision-guide.md BEFORE choosing a hook. The linter lists possible hooks in no particular order — do NOT pick the first suggestion. The guide contains property-based rules that determine the correct hook.

Non-color hooks are simpler — match the CSS value to the numbered scale. See non-color-hooks-decision-guide.md for value-to-hook lookup tables covering spacing, sizing, typography, borders, radius, and shadows.

Step 5: Validate and Verify

Linter feedback loop — repeat until zero errors:

1. npx @salesforce-ux/slds-linter@latest lint .
2. Review errors -> fix by type (Step 3)
3. Re-run linter
4. Repeat until output shows: 0 errors

Validation

  • No .slds-* classes in CSS selectors
  • No var(--lwc-*) tokens without SLDS 2 replacements
  • All hooks include fallback values
  • Background/foreground color hooks from same family
  • Original SLDS classes preserved in HTML
  • Spacing uses numbered hooks (not named like spacing-medium)
  • Typography uses numbered hooks (not named like font-weight-bold)
  • Component renders correctly in light/dark mode and density settings

See migration-checklist.md for the full validation checklist.


Output

Return the fully migrated CSS (and updated HTML markup where class overrides were fixed) with zero SLDS linter violations. All styling hooks must include fallback values preserving the original CSS values.


Advanced Patterns

Color-Mix for Transparency

When a hardcoded value uses rgba() or transparency, use color-mix() with the SLDS hook to preserve opacity:

/* Before */
border-color: rgba(186, 5, 23, 0.7);

/* After — use oklab color space for perceptual consistency */
border-color: color-mix(in oklab, var(--slds-g-color-palette-red-40, rgb(181,54,45)), transparent 30%);

Formula: To achieve X% opacity, use (100 - X)% transparent in color-mix.

  • 70% opacity → transparent 30%
  • 50% opacity → transparent 50%

Use opaque rgb() as fallback (not rgba()) — color-mix handles the transparency.

calc() Expressions with Tokens

When migrating t('calc(...)') or calc() with deprecated tokens:

/* Before — Aura t() with calc */
height: t('calc(' + lineHeightButton + ' + 2px)');

/* After — if calc is still needed */
height: calc(var(--lwc-lineHeightButton) + 2px);

/* After — if calc was unnecessary, simplify */
height: var(--lwc-lineHeightButton);

For calc() with --lwc-* tokens being replaced:

/* Before */
padding: calc(var(--lwc-spacingMedium) + 4px);

/* After */
padding: calc(var(--slds-g-spacing-4, var(--lwc-spacingMedium)) + 4px);

Tip: Often the calc() is unnecessary and can be simplified. Check if the result matches an existing hook value.


Key Constraints

  • Never invent hook names — only use hooks documented in the SLDS design system
  • Always include fallback values — the fallback must be the exact original value from the source CSS
  • Never change hardcoded numerical values — values like 100%, 50%, 200px, 1.5, auto, 0, inherit, none, flex: 1 are structural/layout values. Do not replace them with hooks and do not remove them — they are not styling hook candidates
  • No exact match? Leave as-is — if a hardcoded value doesn't closely correspond to any hook's rendered value, leave it unchanged rather than force-fitting
  • Match hook number to original value intensity — don't default to -1. Pick the variant closest to the original. See color-hooks-decision-guide.md
  • Only numbered scales — named hooks like spacing-medium, font-weight-bold, radius-large do NOT exist

Troubleshooting

Issue Solution
Linter suggests 2+ color hook options Inspect HTML context to determine element's semantic role — see color-hooks-decision-guide.md
Visual appearance changed after migration Verify fallback values match originals; check surface vs container family
No hook available for hardcoded value Leave unchanged; do not invent custom hook names
Linter says "Remove the static value" for 100%, auto, etc. Leave unchanged — these are layout values. Removing them breaks rendering.
CSS class naming errors Use exact camelCase component name: myComponent-button, not MyComponent-button
Spacing/sizing doesn't match Check value-to-hook mapping in non-color-hooks-decision-guide.md; verify spacing vs sizing usage
Named hook not working (e.g., spacing-medium) Named hooks don't exist — use numbered scale: spacing-4 for 16px, font-weight-7 for inline bold emphasis (not headings)
Component looks different in compact density Use density-aware hooks (--slds-g-spacing-var-*) for components that adapt to density

References

用于ISV获取应用分析数据,支持创建查询请求、配置模拟模式及理解生命周期。适用于包使用数据检索和订阅者快照查询,排除标准报表或常规SOQL查询场景。
用户询问App Analytics相关功能 需要检索托管包使用数据 配置App Analytics模拟模式 查询订阅者快照 提及AppAnalyticsQueryRequest或AppAnalyticsSettings
skills/dx-app-analytics-query/SKILL.md
npx skills add forcedotcom/sf-skills --skill dx-app-analytics-query -g -y
SKILL.md
Frontmatter
{
    "name": "dx-app-analytics-query",
    "metadata": {
        "version": "1.0",
        "minApiVersion": "56.0"
    },
    "description": "ISV App Analytics metadata types — AppAnalyticsQueryRequest and AppAnalyticsSettings. Use this skill when the user asks about retrieving managed package usage data, configuring App Analytics simulation mode, querying subscriber snapshots, or understanding the AppAnalyticsQueryRequest lifecycle (New → Pending → Complete → Expired). TRIGGER when: user mentions App Analytics, AppAnalyticsQueryRequest, AppAnalyticsSettings, package usage data, subscriber analytics, ISV analytics, or simulation mode for app analytics. DO NOT TRIGGER when: the task is about standard Salesforce reports\/dashboards (use reporting skills), custom SOQL on Account\/Contact (use platform-soql-query), or Data Cloud query\/search (use data360-query)."
}

App Analytics

When This Skill Owns the Task

Use dx-app-analytics-query when the work involves:

  • Creating AppAnalyticsQueryRequest records via the REST/sObject API
  • Configuring AppAnalyticsSettings via the Metadata API (simulation mode, opt-out)
  • Understanding the query lifecycle: New → Pending → Complete → Expired → Failed
  • Choosing between dataType values: PackageUsageSummary, PackageUsageLog, SubscriberSnapshot
  • File format and compression options for analytics downloads
  • Time-range filtering with startTime, endTime, availableSince
  • Troubleshooting failed or expired analytics queries

Delegate elsewhere when the user is:

  • Running standard CRM SOQL queries → platform-soql-query
  • Working with Data Cloud SQL or DMOs → data360-query
  • Building reports/dashboards on standard objects → reporting skills
  • Deploying or retrieving generic metadata XML → platform-metadata-deploy / retrieving-metadata

Available Types

AppAnalyticsQueryRequest (REST/sObject API)

An asynchronous query request that ISV partners use to retrieve usage analytics data for their managed packages from the ISV Intelligence Data Lake. Records are created via POST /services/data/vXX.0/sobjects/AppAnalyticsQueryRequest and polled via GET /services/data/vXX.0/sobjects/AppAnalyticsQueryRequest/<id>. The system processes the query and provides a presigned download URL upon completion.

Fields (14 properties):

Field Type Description
DataType string (filterable) Type of analytics data. Values: PackageUsageSummary, PackageUsageLog, SubscriberSnapshot
RequestState string (filterable) Processing status. Values: New, Pending, Complete, Expired, Failed, NoData, Delivered
StartTime string Start of time range for requested data
EndTime string End of time range. Should be set on an hour boundary
AvailableSince string Limits query to data indexed after this time (inclusive). Use for incremental retrieval
PackageIds string Comma-delimited list of managed package IDs (033-prefix)
OrganizationIds string Comma-delimited list of subscriber org IDs to filter results
DownloadUrl string Presigned URL for downloading results. Populated when RequestState is Complete
DownloadSize long Size in bytes of the result data file
DownloadExpirationTime string Time at which the download URL expires
FileType string (filterable) Output format. Values: csv, parquet
FileCompression string (filterable) Compression. Values: none, gzip, snappy
QuerySubmittedTime string Time the query was submitted to the Data Lake
ErrorMessage string Diagnostic message for failed queries

AppAnalyticsSettings (Metadata API)

Configuration settings for ISV App Analytics that control simulation mode and opt-out behavior. Deployed via the Metadata API (sf project deploy) or Tooling API.

Fields (2 properties):

Field Type Description
enableSimulationMode boolean (filterable) When true, allows querying sample usage logs for integration testing without real subscriber data
enableAppAnalyticsOptOut boolean (filterable) When true, opts this subscriber org out of AppExchange App Analytics data collection

Request Lifecycle

New → Pending → Complete → (download within expiration window)
                         → Expired (download URL no longer valid)
                         → Delivered (download confirmed received)
         → Failed (check errorMessage)
         → NoData (no matching records for the criteria)

Common Patterns

Query Package Usage Summary (last 7 days)

Create a record via the REST API:

POST /services/data/v60.0/sobjects/AppAnalyticsQueryRequest
Content-Type: application/json

{
  "DataType": "PackageUsageSummary",
  "StartTime": "<7-days-ago>T00:00:00Z",
  "EndTime": "<today-on-hour-boundary>T00:00:00Z",
  "PackageIds": "033XXXXXXXXXXXX",
  "FileType": "csv",
  "FileCompression": "gzip"
}

Poll the record via GET until RequestState reaches Complete, then download from DownloadUrl.

Incremental Data Retrieval

Set AvailableSince to the timestamp of your last successful query completion to avoid re-downloading data you already have.

Enable Simulation Mode for Testing

Deploy AppAnalyticsSettings via Metadata API with enableSimulationMode: true to query sample data without real subscribers.


High-Signal Gotchas

  • AppAnalyticsQueryRequest is an sObject — create and poll records via the REST Data API (/sobjects/AppAnalyticsQueryRequest), NOT via Metadata API XML deployment.
  • AppAnalyticsQueryRequest does NOT have Apex triggers and does NOT flow through custom objects or Flows.
  • Download URLs expire — always check DownloadExpirationTime before attempting download.
  • EndTime should be set on an hour boundary for consistent results.
  • AvailableSince is inclusive — data indexed at exactly that timestamp will be included.
  • PackageIds uses 033-prefix IDs, not 04t (package version) IDs.
  • Data is processed asynchronously by the ISV Intelligence Data Lake infrastructure. There is no synchronous query option.
  • FileType: parquet with FileCompression: snappy gives optimal performance for large datasets.

Output Format

Analytics task: <query / configure / troubleshoot>
Data type: <PackageUsageSummary / PackageUsageLog / SubscriberSnapshot>
Package IDs: <033-prefixed IDs>
Time range: <startTime> to <endTime>
File format: <csv|parquet> / <none|gzip|snappy>
Request state: <current state>
Next step: <poll for completion / download / investigate failure>
负责Salesforce Code Analyzer的安装、配置及CI/CD集成。处理prerequisites检查、code-analyzer.yml创建与编辑、引擎规则调整、忽略模式设置及故障排查,不包含扫描执行或违规修复。
set up code analyzer configure code analyzer install code analyzer code analyzer not working fix my setup scan failing check my setup enable/disable engine exclude files change severity set up GitHub Actions set up CI/CD add to pipeline pipeline fail update my workflow quality gate fail on violations scan changed files only add SARIF code-analyzer.yml ESLint config increase SFGE memory
skills/dx-code-analyzer-configure/SKILL.md
npx skills add forcedotcom/sf-skills --skill dx-code-analyzer-configure -g -y
SKILL.md
Frontmatter
{
    "name": "dx-code-analyzer-configure",
    "metadata": {
        "version": "1.0",
        "cliTools": [
            {
                "tool": [
                    "sf"
                ],
                "semver": ">=2.0.0"
            },
            {
                "tool": [
                    "java"
                ],
                "semver": ">=11.0.0"
            },
            {
                "tool": [
                    "node"
                ],
                "semver": ">=18.0.0"
            },
            {
                "tool": [
                    "python3"
                ],
                "semver": ">=3.10.0"
            },
            {
                "tool": [
                    "git"
                ],
                "semver": ">=2.0.0"
            },
            {
                "tool": [
                    "npm"
                ],
                "semver": ">=9.0.0"
            }
        ],
        "relatedSkills": [
            "dx-code-analyzer-run",
            "dx-code-analyzer-custom-rule-create"
        ]
    },
    "description": "Set up, configure, and troubleshoot Salesforce Code Analyzer for any project. Handles installation, prerequisite checks, diagnosing broken setups, creating and editing code-analyzer.yml overrides, engine-specific settings, ignore patterns, severity overrides, and CI\/CD pipeline setup. TRIGGER when: user says 'set up code analyzer', 'configure code analyzer', 'install code analyzer', 'code analyzer not working', 'fix my setup', 'scan failing', 'check my setup', 'enable\/disable engine', 'exclude files', 'change severity', 'set up GitHub Actions', 'set up CI\/CD', 'add to pipeline', 'pipeline fail', 'update my workflow', 'quality gate', 'fail on violations', 'scan changed files only', 'add SARIF', 'code-analyzer.yml', 'ESLint config', 'increase SFGE memory', or reports errors running Code Analyzer. DO NOT TRIGGER when: user wants to run a scan (use dx-code-analyzer-run), fix violations, explain rules, create custom rules (use dx-code-analyzer-custom-rule-create), or suppress violations."
}

Configuring Code Analyzer Skill

Overview

Ecosystem: This skill is part of a 3-skill Code Analyzer suite — dx-code-analyzer-run (scans & results) · dx-code-analyzer-configure (setup, config, CI/CD) · dx-code-analyzer-custom-rule-create (custom rule authoring).

This skill manages the code-analyzer.yml configuration file — the single source of truth for how Code Analyzer behaves in a project. All customization (engines, rules, ignores, suppressions) is done by creating or editing this file. If the file doesn't exist, this skill creates it in the current working directory.


Scope

In scope:

  • Checking prerequisites (sf CLI, Java, Node.js, Python, org auth)
  • Installing/updating the Code Analyzer plugin
  • Creating code-analyzer.yml if it doesn't exist
  • Editing code-analyzer.yml for all configuration changes
  • Engine settings, rule overrides, ignore patterns, suppressions
  • CI/CD pipeline setup (GitHub Actions, Jenkins, etc.)
  • Environment validation and troubleshooting

Out of scope:

  • Running scans → use dx-code-analyzer-run skill
  • Fixing violations, explaining rules, suppression management → use dx-code-analyzer-run skill
  • Creating custom rules → use dx-code-analyzer-custom-rule-create skill

Tool Usage Rules

Allowed: Bash (sf, java, node, python3, git, npm), Read, Write, Edit Forbidden: MCP tools, Agent tool, Web tools, other skills, which, find, locate, searching for binaries


Core Principle: YAML Only When Customizing

Code Analyzer works out of the box with NO config file — all defaults are built into the tool. The code-analyzer.yml file is ONLY created when the user explicitly requests a customization.

Rules:

  • Do NOT create code-analyzer.yml proactively — only when user asks to change something
  • Do NOT duplicate built-in defaults — only write entries that intentionally override behavior
  • Always place at project root — where sfdx-project.json or sf-project.json lives
  • The CLI auto-discovers itsf code-analyzer run from project root automatically picks up code-analyzer.yml in that directory. No --config-file flag needed.
  • User says "configure code analyzer" with no specifics? → Ask what they want to customize. Don't create an empty or boilerplate file.

Workflow:

  1. User requests a customization (e.g., "disable PMD", "ignore test files", "increase SFGE memory")
  2. Check if code-analyzer.yml exists at project root
  3. If NO → create it at project root with ONLY the requested override
  4. If YES → read it, then edit in the requested change
  5. Validate with sf code-analyzer config

Step 1: Understand Intent and Map to Config Sections

The user can request ANY combination of configuration changes in natural language. Your job is to:

  1. Parse what they want — may be one thing or many things combined
  2. Map each request to the correct section(s) of code-analyzer.yml
  3. Create the file if it doesn't exist, then apply all changes

The code-analyzer.yml Structure (what you can write/edit)

config_root: .                    # Root for relative path resolution
log_folder: <path>                # Where logs are written
log_level: <1-5>                  # 1=Error, 2=Warn, 3=Info, 4=Debug, 5=Fine

ignores:                          # Files/folders excluded from scanning
  files: [<glob patterns>]

engines:                          # Per-engine settings
  <engine_name>:
    disable_engine: <bool>
    <engine_specific_keys>: ...

rules:                            # Per-rule overrides
  <engine_name>:
    <rule_name>:
      severity: <1-5>
      tags: [<strings>]
      disabled: <bool>

suppressions:                     # Bulk suppression configuration
  disable_suppressions: <bool>
  "<file_or_folder_path>":
    - rule_selector: "<selector>"
      max_suppressed_violations: <number|null>
      reason: "<why>"

Mapping Principle

Any user request maps to one or more sections above. Parse the intent and edit the right section(s):

Intent Category Maps To Examples of What User Might Say
Setup / Install Step 2 (prerequisites + install) "set up", "install", "get started", "new laptop", "from scratch"
Diagnose / Fix Step 2A (systematic debug) "not working", "broken", "fix my setup", "scan fails", "getting errors"
Engine control engines.<name>.disable_engine "disable X", "turn off Y", "only use Z", "enable all"
Engine tuning engines.<name>.<property> "increase memory", "change heap", "use my eslint config", "set tokens to 50"
File exclusions ignores.files "exclude", "ignore", "skip", "don't scan X"
Rule severity rules.<engine>.<rule>.severity "make X critical", "promote", "demote", "change severity"
Rule disable rules.<engine>.<rule>.disabled "disable rule X", "turn off Y rule", "remove Z"
Rule tags rules.<engine>.<rule>.tags "tag X as security", "add recommended tag"
Suppressions suppressions section "suppress X in folder Y", "allow N violations"
CI/CD Generate pipeline file (separate from config) "github actions", "CI", "quality gate"
View/inspect Read file + sf code-analyzer config "show config", "what's configured", "current settings"

File Existence Decision

BEFORE editing anything, check if code-analyzer.yml exists at project root:

ls code-analyzer.yml code-analyzer.yaml 2>/dev/null
  • File does NOT exist → Create it at project root with ONLY the user's requested override(s)
  • File exists → Read it, then Edit to add/modify the requested section(s)

The CLI auto-discovers code-analyzer.yml in the current directory. Since scans run from project root, the file must live there.

Rule Name Resolution — ALWAYS Before Writing YAML

When a user references rules by partial, descriptive, or approximate names (e.g., "the doc rule", "CRUD violation", "console rule", "hardcoded values"), you MUST resolve to exact rule names using the lookup in Step 6.1 BEFORE writing any YAML. The code-analyzer.yml file silently ignores rule names that don't exactly match — there is no error, the override just won't apply.

Examples of fuzzy → exact resolution needed:

  • "Disable the ApexDoc rule" → lookup confirms ApexDoc (engine: pmd)
  • "Demote no-console to low" → lookup confirms no-console (engine: eslint)
  • "Make CRUD violations critical" → lookup confirms ApexCRUDViolation (engine: pmd)
  • "Turn off the hardcoded values check" → lookup finds @salesforce-ux/slds/no-hardcoded-values-slds2 (engine: eslint)
  • "Disable the injection rule" → multiple matches possible → ask user which one

Only skip the lookup when the user provides an unambiguous, exact, well-known name (e.g., "ApexDoc", "no-console", "no-unused-vars").

Handling Combined/Complex Requests

Users will often combine multiple changes in one request. Handle ALL of them in a single edit:

  • "Disable PMD's ApexDoc rule and make CRUD violations critical" → edit two entries under rules.pmd
  • "Exclude test files and vendor code, and increase SFGE memory" → edit ignores.files + engines.sfge.java_max_heap_size
  • "Set up code analyzer with only ESLint and PMD, ignore node_modules" → create file with engines (disable others) + ignores
  • "Make all security rules severity 1" → look up rules via sf code-analyzer rules --rule-selector Security, then override each
  • "Configure code analyzer" (no specifics) → ask user what they want to customize before creating any file

Quick Reference: Common Requests → Config Output

User Says Resulting YAML
"configure code analyzer" Ask user what to customize — don't create file until there's an actual override
"disable the ApexDoc rule" rules: pmd: ApexDoc: disabled: true
"only scan Apex, no JavaScript" engines: eslint: disable_engine: true + engines: retire-js: disable_engine: true
"ignore all test files" ignores: files: ["**/test/**", "**/__tests__/**", "**/*.test.js"]
"make security rules critical" Look up rules, then rules: <engine>: <rule>: severity: 1 for each
"increase SFGE memory to 8g" engines: sfge: java_max_heap_size: "8g"
"use my project's ESLint config" engines: eslint: auto_discover_eslint_config: true
"suppress CRUD violations in legacy folder" suppressions: "force-app/legacy/": [{rule_selector: "pmd:ApexCRUDViolation", reason: "..."}]

The AI must understand the YAML schema and write valid config for ANY request, not just the examples above.


Step 2: Check Prerequisites and Install

Run bash "<skill_dir>/scripts/check-prerequisites.sh" or check manually:

sf --version 2>&1                                    # sf CLI
sf plugins --core 2>&1 | grep -i "code-analyzer"    # Plugin
java -version 2>&1                                   # Java 11+ (PMD, CPD, SFGE)
node --version 2>&1                                  # Node 18+ (ESLint, RetireJS)
python3 --version 2>&1                               # Python 3 (Flow engine)

If anything is missing, install it (always ask user first):

npm install -g @salesforce/cli                       # sf CLI
sf plugins install @salesforce/plugin-code-analyzer  # Code Analyzer plugin

For Java/Node/Python installs, read <skill_dir>/references/engine-prerequisites.md. If install fails, read <skill_dir>/references/troubleshooting.md.


Step 2A: Diagnose and Fix a Broken Setup

TRIGGER: User says "not working", "broken", "getting errors", "scan fails", "help me fix", etc.

Read <skill_dir>/references/diagnostic-flow.md for the complete layered diagnostic procedure, fix table, and anti-patterns.

Key principles (always apply):

  • Never search for binaries (which, find, ls /opt/homebrew/bin/)
  • Never use sfdx as a workaround — only sf
  • Fix layer by layer: CLI → Plugin → Engine deps → verify scan
  • Give user ONE command at a time, wait for confirmation before continuing
  • After fix succeeds, proceed to run the full scan automatically

Step 3: Create or Edit code-analyzer.yml

Only triggered when user requests a customization. Never create proactively.

Creating (file doesn't exist)

Choose one of the two approaches below — do not run both:

Option A — Auto-generate from project type (recommended for first-time setup):

Run bash "<skill_dir>/scripts/generate-config.sh". This detects Apex, LWC, and Flow markers and produces a minimal code-analyzer.yml suited to the project. Skip to the "After any create/edit, validate" section.

Note: The script exits with an error if code-analyzer.yml already exists. Delete the existing file first if you need to regenerate.

Option B — Write manually (when the user has specific customizations in mind):

Read the appropriate example config as a reference for structure:

  • For Apex-only projects, read <skill_dir>/examples/apex-project-config.yml
  • For LWC-only projects, read <skill_dir>/examples/lwc-project-config.yml
  • For full-stack (Apex + LWC + Flows), read <skill_dir>/examples/fullstack-project-config.yml

Write the file at project root using the Write tool. Include ONLY the user's requested changes:

# Example: user said "ignore test files and increase SFGE memory"
# → Write to project root (where sfdx-project.json lives):
ignores:
  files:
    - "**/test/**"
    - "**/__tests__/**"

engines:
  sfge:
    java_max_heap_size: "4g"

Do NOT add config_root, log_folder, or any other field the user didn't ask for.

Editing (file already exists)

Read the file, then use the Edit tool to add/modify only the relevant section. Preserve everything else.

After any create/edit, validate:

Run bash "<skill_dir>/scripts/validate-config.sh" to validate YAML syntax and schema correctness, or use the CLI directly:

sf code-analyzer config

(No --config-file needed — the CLI auto-discovers code-analyzer.yml in CWD.)

If user says "configure code analyzer" with no specifics

Ask: "What would you like to customize? For example: ignore certain files, change rule severities, tune engine settings, or disable engines you don't need."


Step 4: Enable/Disable Engines

Edit the engines section in code-analyzer.yml:

engines:
  pmd:
    disable_engine: true       # Disable PMD
  eslint:
    disable_engine: false      # Enable ESLint (default)

Valid engine names: pmd, cpd, eslint, regex, retire-js, flow, sfge, apexguru

Always validate after editing:

sf code-analyzer config --config-file code-analyzer.yml

Step 5: Ignore Patterns

Edit the ignores section in code-analyzer.yml:

ignores:
  files:
    - "**/node_modules/**"
    - "**/.sfdx/**"
    - "**/.sf/**"
    - "**/vendor/**"
    - "**/*.min.js"

Common patterns:

Pattern Excludes
**/node_modules/** npm dependencies
**/.sfdx/**, **/.sf/** SF CLI internals
**/test/**, **/__tests__/** Test directories
**/*.test.js, **/*.spec.js Test files
**/jest-mocks/** Jest mocks
**/vendor/**, **/*.min.js Third-party/minified
**/staticresources/** Static resources

Step 6: Rule Overrides

Edit the rules section in code-analyzer.yml. Each rule can have severity, tags, and disabled overrides:

rules:
  pmd:
    ApexCRUDViolation:
      severity: 1              # Promote to Critical
    AvoidGlobalModifier:
      disabled: true           # Turn off entirely
    ApexDoc:
      severity: 5              # Demote to Info
      tags: ["Documentation"]
  eslint:
    no-console:
      severity: 4              # Demote to Low
    no-unused-vars:
      severity: 2              # Promote to High

Severity values: 1/Critical, 2/High, 3/Moderate, 4/Low, 5/Info

6.1 Rule Name Resolution (Fuzzy Matching)

⚠️ CRITICAL: A misspelled or partial rule name in code-analyzer.yml is SILENTLY IGNORED — no error, the override just won't apply.

When users reference rules by approximate names (e.g., "the doc rule", "CRUD violation", "hardcoded values"), resolve to exact names BEFORE writing YAML:

sf code-analyzer rules --rule-selector all 2>&1 | grep -i "<USER_KEYWORD>"
  • 1 match → use that exact name + its engine for the YAML path
  • Multiple matches → ask user which one they meant
  • 0 matches → try broader keywords or inform user

Skip the lookup only when the name is unambiguous and exact (e.g., "ApexDoc", "no-console", "no-unused-vars").

For detailed matching strategies, common fuzzy→exact mappings, and engine identification: Read <skill_dir>/references/rule-name-resolution.md.


Step 7: Engine-Specific Settings

Edit the engines section. Most common overrides:

engines:
  sfge:
    java_max_heap_size: "4g"      # <200 classes→"2g", 200-500→"4g", 500+→"6g"/"8g"
    java_thread_count: 4
    java_thread_timeout: 900000
  eslint:
    auto_discover_eslint_config: true    # Use project's own ESLint config
    eslint_config_file: "./eslint.config.mjs"
  pmd:
    custom_rulesets: ["./config/custom-pmd-rules.xml"]
    java_classpath_entries: ["./lib/custom-rules.jar"]
  cpd:
    minimum_tokens: { apex: 100, javascript: 100 }
  apexguru:
    target_org: "my-org-alias"
  flow:
    python_command: "python3"
  # regex.custom_rules — use the dx-code-analyzer-custom-rule-create skill to create these.
  # Never hand-write regex patterns into code-analyzer.yml: quotes/backslashes
  # inside YAML cause parsing failures. The create-regex-rule.js script handles
  # serialization correctly and must always be used for regex rule creation.

For full property list per engine, read <skill_dir>/references/config-schema.md.


Step 8: CI/CD Pipeline Setup

Detect CI system from workspace (.github/workflows/ → GitHub Actions, Jenkinsfile → Jenkins, etc.). Read <skill_dir>/references/ci-cd-templates.md for templates. Use <skill_dir>/examples/ci-github-actions.yml as GitHub Actions base. Key flags: --severity-threshold 2 (gate), --output-file results.sarif (GitHub scanning), --config-file code-analyzer.yml.


Step 9: View Current Configuration

sf code-analyzer config                               # Show effective config
sf code-analyzer config --rule-selector pmd:Security  # Specific rules
sf code-analyzer config --include-unmodified-rules    # All defaults

Cross-Skill Integration

This skill works together with dx-code-analyzer-run. The AI agent should seamlessly hand off between them:

When dx-code-analyzer-run delegates HERE:

If a user says "scan my code" / "run code analyzer" but it fails (CLI missing, plugin not installed, or scan errors out), dx-code-analyzer-run delegates to this skill. In that case:

  1. Run the diagnose and fix flow (Step 2A) — find what's broken, fix it
  2. After everything works, automatically proceed to run the scan — do not stop and ask. The user's original intent was to scan.
  3. Hand execution back to dx-code-analyzer-run behavior (build command, execute, parse results).

When THIS skill hands off to dx-code-analyzer-run:

After any successful configuration action, offer to run a scan (e.g., "Setup complete! Want me to run a scan?", "Config updated — want to scan and verify?"). If user says yes, proceed with dx-code-analyzer-run behavior.

When THIS skill hands off to dx-code-analyzer-custom-rule-create:

If the user asks to create a custom rule while you are working on configuration (e.g., "also add a rule that bans System.debug", "set up a PMD XPath rule"), delegate to dx-code-analyzer-custom-rule-create. When that skill finishes, the custom_rulesets or eslint_config_file pointer in code-analyzer.yml may need to be set — that edit belongs here, in this skill.

When user intent spans BOTH skills:

Handle end-to-end: "not working" → Diagnose → Fix → Scan. "Set up and scan" → Install → Scan. "Disable ESLint and scan Apex" → Edit config → Run with --rule-selector pmd. "Configure custom rules and scan" → dx-code-analyzer-custom-rule-create → wire config → dx-code-analyzer-run. Always follow through to the user's final intent.


Rules / Constraints

Constraint Rationale
Only create YAML when user requests a customization Defaults work without any file — don't create boilerplate
Place YAML at project root only CLI auto-discovers code-analyzer.yml from CWD
Write only overrides, never duplicate defaults Keep file minimal and intentional
Use Write tool to create, Edit tool to modify Preserves existing settings
Validate after every change sf code-analyzer config catches YAML errors
Ask before installing prerequisites Never auto-install without consent
Never delete existing config without asking User may have custom settings
After setup, offer to scan Close the loop — config without scan is incomplete

Gotchas

Issue Solution
Config not picked up Must be code-analyzer.yml in CWD or use --config-file
YAML validation fails Spaces only (no tabs), check colon spacing
SFGE out of memory Increase java_max_heap_size in engines section
ESLint rules missing Set auto_discover_eslint_config: true

For full troubleshooting, read <skill_dir>/references/troubleshooting.md.


Reference File Index

<skill_dir> is the absolute path to the directory containing this SKILL.md file.

File Purpose
<skill_dir>/scripts/check-prerequisites.sh Environment check
<skill_dir>/scripts/generate-config.sh Auto-detect project type and generate config
<skill_dir>/scripts/validate-config.sh Validate YAML after changes
<skill_dir>/references/config-schema.md Full YAML schema documentation
<skill_dir>/references/diagnostic-flow.md Step 2A: layered diagnostic procedure and fix table
<skill_dir>/references/rule-name-resolution.md Step 6.1: fuzzy rule name lookup strategies and mappings
<skill_dir>/references/engine-prerequisites.md Install instructions per engine
<skill_dir>/references/ci-cd-templates.md CI/CD pipeline templates
<skill_dir>/references/troubleshooting.md Common setup issues and fixes
<skill_dir>/examples/apex-project-config.yml Config for Apex-only project
<skill_dir>/examples/lwc-project-config.yml Config for LWC-only project
<skill_dir>/examples/fullstack-project-config.yml Config for Apex + LWC + Flows
<skill_dir>/examples/ci-github-actions.yml GitHub Actions workflow
用于创建自定义代码分析规则,支持Regex、PMD和ESLint引擎。适用于强制编码标准、禁止特定模式(如硬编码)、检测元数据违规或覆盖内置规则阈值等场景。
用户希望创建新的自定义规则以强制执行未涵盖的编码标准 需要禁止特定代码模式(如System.debug、硬编码ID) 要求编写XPath或Regex规则进行静态分析 需要针对LWC/JS添加ESLint规则或治理元数据
skills/dx-code-analyzer-custom-rule-create/SKILL.md
npx skills add forcedotcom/sf-skills --skill dx-code-analyzer-custom-rule-create -g -y
SKILL.md
Frontmatter
{
    "name": "dx-code-analyzer-custom-rule-create",
    "metadata": {
        "version": "1.0",
        "cliTools": [
            {
                "tool": [
                    "sf"
                ],
                "semver": ">=2.0.0"
            },
            {
                "tool": [
                    "node"
                ],
                "semver": ">=18.0.0"
            },
            {
                "tool": [
                    "npm"
                ],
                "semver": ">=9.0.0"
            }
        ],
        "relatedSkills": [
            "dx-code-analyzer-run",
            "dx-code-analyzer-configure"
        ]
    },
    "description": "Create custom Code Analyzer rules for Regex (pattern matching), PMD (XPath\/AST for Apex and metadata XML), and ESLint (LWC\/JavaScript\/TypeScript). Use when users want to enforce coding standards, ban patterns, detect hardcoded values, govern metadata, or add rules not in the built-in set. TRIGGER when: user says 'create a rule', 'ban System.debug', 'enforce naming convention', 'detect hardcoded IDs', 'custom rule', 'xpath rule', 'regex rule', 'add a PMD rule', 'enforce a policy', 'create a check for', 'flag this pattern', 'make a rule that catches', 'metadata rule', 'check permissions', 'enforce API version', 'eslint rule', 'lwc rule', 'override rule threshold', 'customize complexity', or describes a pattern to enforce. DO NOT TRIGGER when: user wants to run a scan (use dx-code-analyzer-run), configure engines (use dx-code-analyzer-configure), or explain existing rules (use dx-code-analyzer-run)."
}

dx-code-analyzer-custom-rule-create: Custom Code Analyzer Rule Authoring

Ecosystem: This skill is part of a 3-skill Code Analyzer suite — dx-code-analyzer-run (scans & results) · dx-code-analyzer-configure (setup, config, CI/CD) · dx-code-analyzer-custom-rule-create (custom rule authoring).

Use this skill when the user needs to create a custom rule that enforces a pattern not covered by Code Analyzer's built-in rules. Supports Regex engine (text pattern matching) and PMD engine (structural XPath queries against the AST).

When This Skill Owns the Task

Use dx-code-analyzer-custom-rule-create when the work involves:

  • Creating a new custom rule for Code Analyzer (any engine)
  • Enforcing team-specific coding standards via static analysis
  • Banning specific patterns (System.debug, hardcoded IDs, TODOs)
  • Writing XPath expressions for PMD rules (Apex or metadata XML)
  • Writing regex patterns for the Regex engine
  • Setting up custom ESLint rules/plugins for LWC/JavaScript
  • Enforcing metadata governance (API versions, field descriptions, dangerous permissions)
  • Overriding built-in rule thresholds (CyclomaticComplexity, ExcessiveParameterList, etc.)
  • Organizing multiple rules into shared rulesets
  • Iterating on a custom rule that isn't matching correctly

Delegate elsewhere when the user is:

  • Running a scan against existing rules → dx-code-analyzer-run skill
  • Configuring engines, prerequisites, CI/CD → dx-code-analyzer-configure skill
  • Explaining what an existing built-in rule means → dx-code-analyzer-run skill
  • Writing Apex code or tests → generating-apex / running-apex-tests skills

Required Context to Gather First

Ask for or infer:

  • What pattern to catch — what code should be flagged? (If user selected code in their IDE, the selection IS the answer — do not re-ask.)
  • What to allow — any exceptions? (test classes, specific contexts)
  • File scope — which file types? (.cls, .trigger, .js, all?)
  • Severity — how critical? (default: 3/Moderate)

If the user selected code (IDE selection context present), treat it as the pattern definition. Skip clarification unless genuinely ambiguous about what aspect of the selection to target.

If the request is vague with NO selection ("add a rule for best practices"), ask ONE clarifying question:

"What specific pattern should this rule flag?"


Hard Constraints

These are non-negotiable rules. Violating any of them is a skill failure regardless of whether the output happens to work.

  1. ALWAYS run ast-dump before writing XPath. No exceptions. Do not use node names from memory, references, or prior conversations. The AST is the source of truth — run sf code-analyzer ast-dump, read the output, then write XPath that matches what you see. Even for "well-known" patterns like SOQL-in-loop, run ast-dump first. If you skip this step and the rule works, it is still a process failure.

  2. ALWAYS use the scripts to create rules. For regex rules, ALWAYS use create-regex-rule.js. For PMD rules, ALWAYS use create-pmd-rule.js. Do NOT manually edit code-analyzer.yml to add rule definitions — regex patterns in YAML cause escaping failures (quotes inside quotes, backslashes getting eaten). The scripts handle YAML serialization correctly every time.

  3. NEVER manually edit code-analyzer.yml after a script writes to it — even to fix a bad value. The scripts produce correctly-escaped YAML. If you then rewrite or restructure the file, you WILL break the escaping. If the user added top-level config (like ignores.files), leave it alone too — only touch what you wrote.

    If a script's output looks wrong (rule fails to validate, YAML parse error, stray characters in the regex):

    • DO NOT patch the YAML by hand. That is exactly the failure mode this constraint exists to prevent.
    • Always delete the broken rule's entire YAML block, then re-invoke the script with corrected arguments. Removing a block you just wrote does not violate this rule; rewriting fields inside it does.
    • If the script accepted bad input and produced bad output, the input was wrong (e.g., --regex "/.../ g" with a stray space — the flags must be /g exactly, no whitespace). Re-invoke with the corrected argument.
    • If you genuinely believe the script has a bug, STOP and surface it to the user. Do not hand-edit as a workaround.
  4. --regex must be /pattern/flags with NO whitespace. The script trims and validates flags strictly — only g, i, m, s, u, y. /pat/ g (with a space) is rejected; so is /pat/x (invalid flag) and /pat/ (no flags). The global flag g is mandatory. If validation fails, fix the argument — do NOT bypass by writing YAML directly.

  5. ALWAYS validate after creation. Run sf code-analyzer rules --rule-selector <engine>:<name> before testing. If Found 0 rules, the YAML didn't parse — delete the block, fix the argument, re-invoke the script.

  6. ALWAYS test against a sample file. Confirm at least one true positive and one true negative.

    For regex rules, the negative sample MUST NOT contain the pattern text anywhere — including inside comments and string literals. Regex engines scan raw text; // no System.debug here IS a match for /System\.debug/g. Trace your pattern against the negative file mentally before running it.

  7. Create rules ONE AT A TIME, sequentially. When the user requests multiple rules, create each rule individually through the full workflow (create → validate → test positive → test negative) before starting the next one. Do NOT batch-create rules — if one fails, it corrupts the config for all subsequent rules. Complete each rule end-to-end, confirm it works, then move to the next.

  8. For regex rules, exclude test classes via ignores.filesregex_ignore does NOT do this. regex_ignore is a per-LINE filter (the line must match BOTH the rule and the ignore pattern); it cannot exclude an entire test class. If the user's intent is "skip test classes," add a top-level ignores.files block with globs like "**/*Test.cls" AFTER all rules are created — do not interleave config edits with script invocations.


Engine Selection

Pattern Type Engine Why
Text/string pattern (TODO, hardcoded ID, keyword) Regex Simple, fast, no Java needed
Apex code structure (method calls, nesting, SOQL in loops) PMD/XPath (language=apex) Understands AST, not fooled by comments/strings
Metadata XML governance (API version, permissions, descriptions) PMD/XPath (language=xml) Structural XML matching with namespace handling
LWC/JavaScript/TypeScript patterns ESLint* Standard JS tooling, plugin ecosystem
Both could work (Apex/metadata only) Regex first Simpler to create and maintain

* For ESLint: ALWAYS check Tier 1 (built-in rules) and Tier 2 (configurable rules) BEFORE creating a custom plugin. See references/eslint-rules-discovery.md.

⚠️ "Both could work → Regex first" NEVER applies to JavaScript/LWC/TypeScript files. JS/LWC/TS patterns MUST use ESLint — Regex cannot distinguish code from comments/strings in JS and produces false positives. Do NOT rationalize Regex for JS files based on "simplicity" or "no npm dependencies."

Tell the user which engine you chose and why. Respect their preference if they disagree.

Excluding Test Classes — Strategy by Engine

When a rule should NOT apply to test classes, the approach differs by engine:

Engine How to exclude test classes Notes
PMD (Apex) Add [not(ancestor::UserClass[ModifierNode[@Test = true()]])] to the XPath Structural exclusion — works perfectly, no config changes needed
Regex Use ignores.files in code-analyzer.yml with globs like **/*Test.cls regex_ignore is per-LINE, not per-FILE — it CANNOT exclude entire test classes. Only use regex_ignore for per-line patterns like // NOPMD
ESLint Use ignores array in eslint.config.js Standard ESLint file-level ignores

⚠️ regex_ignore is NOT file-level exclusion. It only skips matches on lines that ALSO match the ignore pattern. Example: regex_ignore: "/@isTest/i" only suppresses violations on lines containing @isTest — a SOQL query on line 50 of a test class still flags because line 50 doesn't contain @isTest. To exclude test files from regex rules entirely, use:

ignores:
  files:
    - "**/*Test.cls"
    - "**/*_Test.cls"

⚠️ ignores.files is GLOBAL — it affects ALL engines and ALL rules. If you need test-class exclusion for some rules but not others (e.g., exclude tests from SOQL rules but still scan tests for @AuraEnabled), use PMD with XPath for the rules that need selective exclusion. PMD's XPath can structurally check @Test = true() per-method or per-class — Regex cannot.

Decision guide for Apex rules that should skip test classes:

  • If the pattern is structural (method calls, annotations, nesting) → use PMD. XPath handles test-class exclusion natively.
  • If the pattern is purely textual AND all regex rules should skip tests → use Regex + ignores.files.
  • If you have a mix (some rules skip tests, others don't) → use PMD for the test-sensitive rules, Regex for the others.

Workflow

When User Selects Code (IDE Selection)

When the user highlights a code block in their editor and asks to "catch this", "flag this pattern", "create a rule for this", or similar:

  1. The selection IS your positive sample. Do NOT ask "what pattern should this rule flag?" — the user already showed you. Do NOT write a new sample from scratch.
  2. Identify what's structural vs. incidental in the selection:
    • Structural (rule-worthy): the method call, the loop pattern, the missing keyword, the nesting
    • Incidental (ignore): specific variable names, string values, parameter counts
    • Ask ONE question if ambiguous: "Should the rule catch all System.debug calls, or only those without a LoggingLevel parameter?"
  3. Ast-dump the ACTUAL file the user has open (not a new sample file):
    sf code-analyzer ast-dump --file <the-open-file.cls> --output-file <ast.xml>
    
  4. Find the selection in the AST output — locate the nodes corresponding to the highlighted lines. These are your target nodes.
  5. Generalize the XPath — write XPath that matches the structural pattern, NOT the specific instance. Replace specific variable names with wildcards, keep structural nodes and discriminating attributes.
  6. Continue with standard workflow (negative sample, create rule, validate, test positive + negative).

Example flow:

  • User selects: Database.query('SELECT Id FROM ' + objectName)
  • Structural pattern: Database.query call (dynamic SOQL)
  • Incidental: the specific string concatenation inside
  • Engine: PMD (structural call detection)
  • XPath: //MethodCallExpression[@FullMethodName='Database.query']
  • NOT: regex matching Database.query (would miss multiline, match comments)

Example flow (block selection):

  • User selects a 5-line block with SOQL inside a for-each loop
  • Structural: SOQL query as descendant of loop body
  • Incidental: specific query fields, variable names
  • Engine: PMD
  • Ast-dump the open file → find ForEachStatement + SoqlExpression in body
  • XPath: //ForEachStatement/BlockStatement//SoqlExpression

For Regex Rules

  1. Write a positive sample (5-10 lines) demonstrating the violation. Write sample files inside the project workspace (e.g., a temporary samples/ directory at the project root) so Code Analyzer can target them.
  2. Write a SEPARATE negative sample file — code that looks similar but must NOT be flagged. Test your regex mentally against this file BEFORE creating the rule.
  3. Build and create the rule — read references/regex-rule-schema.md for the complete schema, then run the script:
    node "<skill_dir>/scripts/create-regex-rule.js" \
      --name "<RuleName>" --regex "<pattern>" --description "<desc>" \
      --severity <1-5> --file-extensions ".cls,.trigger"
    
    ⚠️ ALWAYS use the script. Do NOT manually write regex patterns into code-analyzer.yml — regex characters (quotes, backslashes, braces) inside YAML cause parsing failures. The script handles serialization correctly.
  4. Validatesf code-analyzer rules --rule-selector regex:<RuleName>
  5. Test positivesf code-analyzer run --rule-selector regex:<RuleName> --target <violation-sample> — must find violations
  6. Test negativesf code-analyzer run --rule-selector regex:<RuleName> --target <clean-sample> — must find 0 violations. If it flags clean code, your regex is too broad — go back and tighten the pattern.
  7. Iterate if test fails (adjust regex, add regex_ignore, narrow extensions)
  8. Cleanup — delete ALL sample files you created. Do not leave temporary test fixtures in the user's project.

For PMD/XPath Rules (Apex)

  1. Write a minimal sample (5-10 lines) demonstrating the violation. Write sample files inside the project workspace (e.g., a temporary samples/ directory at the project root) so Code Analyzer can target them. After both positive and negative tests pass, delete ALL sample files you created — both the original samples and any copies made during testing. Do not leave temporary test fixtures in the user's project. For loop-based rules, the positive sample MUST include all 3 Apex loop types (for-each, traditional for, and while) — omitting any loop type means the XPath won't be validated against it and may silently miss violations.
  2. ⚠️ MANDATORY: Dump the ASTsf code-analyzer ast-dump --file <sample.cls> --output-file <ast.xml> — this step is NOT optional. Do NOT skip it even if you "already know" the node names. Run it, read the output, confirm the exact node names and attributes.
  3. Read the AST output — identify the target node and its attributes from the ACTUAL ast-dump output (not from memory). Use references/apex-ast-reference.md and references/xpath-patterns.md as supplementary context only.
  4. Write XPath — target the smallest stable node with discriminating attributes. Every node name in your XPath MUST appear verbatim in the ast-dump output you just read. Use /Child (direct child) vs //Descendant deliberately — check the ast-dump to understand which nodes are siblings vs nested.
  5. ⚠️ BEFORE creating the rule: Write a SEPARATE negative sample file (5-10 lines) showing code that is CORRECT and must NOT be flagged. This MUST be a distinct file from the positive sample — do NOT combine positive and negative cases into one file. For loop-based rules, include for (x : [SELECT...]) idiom. Run sf code-analyzer ast-dump on this negative sample file too. Read the output and trace your XPath against it — confirm it does NOT match any node in the negative AST. If it would match, go back to step 4 and tighten the XPath BEFORE proceeding. Do NOT skip this step or defer it until after rule creation. The negative file will be used again in step 9 for an explicit zero-violation confirmation.
  6. Create the rule — run the script:
    node "<skill_dir>/scripts/create-pmd-rule.js" \
      --name "<RuleName>" --xpath "<expression>" --message "<msg>" \
      --language apex --priority <1-5>
    
  7. Validatesf code-analyzer rules --rule-selector pmd:<RuleName>
  8. Test positivesf code-analyzer run --rule-selector pmd:<RuleName> --target <violation-sample.cls> — must find violations
  9. Test negativesf code-analyzer run --rule-selector pmd:<RuleName> --target <clean-sample.cls> — must find 0 violations. If it flags clean code, your XPath is too broad — go back to step 4.
  10. Iterate if test fails (re-examine AST, adjust XPath, check node names)
  11. Cleanup — delete ALL sample files you created (both positive and negative samples, and any ast-dump output files). Do not leave temporary test fixtures in the user's project.

For PMD/XPath Rules (Metadata XML)

  1. Identify the metadata file type (field, permissionset, profile, flow, etc.)
  2. Dump the XML ASTsf code-analyzer ast-dump --file <file>-meta.xml --language xml --output-file <ast.xml>. If ast-dump fails with an error (e.g., "XmlEncoding is not a valid XML name"), fall back to reading the raw XML file directly — the XML DOM structure IS the AST for metadata files (what you see in the file is what PMD sees). Read the file, note element names, nesting, and text content.
  3. Read the DOM structure — confirm element names, nesting, and text content from the ast-dump output OR the raw file. Use references/metadata-xml-rules.md as supplementary context only.
  4. Write XPath for PMD 7 — CRITICAL rules for metadata XML XPath:
    • All element matching MUST use local-name()='ElementName' (namespace blocks bare names)
    • Text content matching MUST use @Text attribute (NOT text() — the text() function does not work in PMD 7's XML language). Example: //*[@Text='ModifyAllData']
    • Navigate from text nodes UP to parent elements using ../.. (text node → element → parent element)
    • Check sibling conditions via .//*[@Text='value'] on the parent
    • See references/metadata-xml-rules.md for the complete PMD 7 XPath pattern
  5. Configure file extensions — PMD's XML language only processes .xml by default. Salesforce metadata files use compound extensions (.permissionset-meta.xml, .field-meta.xml, etc.) but path.extname() returns .xml from these, so .xml is sufficient:
    engines:
      pmd:
        file_extensions:
          xml: [".xml"]
    
    ⚠️ Do NOT add compound extensions like .permissionset-meta.xml — Code Analyzer's validator rejects them (/^[.][a-zA-Z0-9]+$/ pattern). Just .xml covers all Salesforce metadata files automatically.
  6. Write a SEPARATE negative sample file — same as Apex rules: create a metadata file that is correct and must NOT be flagged. Verify the XPath does not match it BEFORE creating the rule. Both positive and negative samples should be .xml extension files in the workspace so PMD can scan them directly.
  7. Create the rule — run the script:
    node "<skill_dir>/scripts/create-pmd-rule.js" \
      --name "<RuleName>" --xpath "<expression>" --message "<msg>" \
      --language xml --priority <1-5>
    
  8. Validatesf code-analyzer rules --rule-selector pmd:<RuleName>
  9. Test positivesf code-analyzer run --rule-selector pmd:<RuleName> --target <violation-sample>.xml — must find violations
  10. Test negativesf code-analyzer run --rule-selector pmd:<RuleName> --target <clean-sample>.xml — must find 0 violations
  11. Iterate if test fails (check @Text vs text(), verify local-name() usage, check file extensions config)
  12. Cleanup — delete ALL sample files you created (both positive and negative samples, and any .xml copies made for testing). Do not leave temporary test fixtures in the user's project.

For ESLint Rules (LWC/JavaScript/TypeScript)

ESLint Discovery Workflow (Read First)

ESLint has 200+ built-in rules plus thousands more from plugins. DO NOT attempt to create a custom plugin before checking if a built-in rule exists. The discovery workflow is:

  1. Tier 1 (Built-In Rules) — 70% of requests

    • Run: sf code-analyzer rules --rule-selector eslint
    • Search for keywords (e.g., "console", "unused", "equal")
    • Check LWC plugin rules (@lwc/lwc/*)
    • If found: Configure and STOP
  2. Tier 2 (Configurable Rules) — 20% of requests

    • Pattern is "ban function X"? → no-restricted-globals
    • Pattern is "ban syntax Y"? → no-restricted-syntax
    • Pattern is "ban property Z"? → no-restricted-properties
    • If applicable: Configure and STOP
  3. Tier 3 (Custom Plugins) — 10% of requests

    • Only for domain-specific multi-node patterns
    • Requires Node.js code, testing, meta.docs metadata
    • See: references/eslint-custom-plugins.md

See: references/eslint-rules-discovery.md for complete discovery guide with examples.


  1. ⚠️ MANDATORY: Run the ESLint discovery workflow FIRST. 90% of ESLint requests are solved by built-in rules (Tier 1) or configurable rules like no-restricted-syntax (Tier 2). Creating a custom plugin (Tier 3) is a LAST RESORT. Before proceeding:

    a) Run: sf code-analyzer rules --rule-selector eslint b) Search the output for keywords from the user's request (e.g., "console", "equal", "unused") c) Check LWC plugin rules: grep for @lwc/lwc/ in the output d) If found: Configure it (see references/eslint-rules-discovery.md) and STOP. Do NOT create a custom plugin. e) If not found: Check if no-restricted-globals, no-restricted-syntax, or no-restricted-properties can express the pattern (Tier 2). f) Only if no Tier 1 or Tier 2 solution exists: Proceed to custom plugin creation.

    Validation: After configuration, run sf code-analyzer rules --rule-selector eslint:<ruleName> to confirm it appears. If it doesn't, the config is wrong.

    ⚠️ Skipping this discovery workflow and creating a custom plugin when a built-in rule exists is a skill failure, regardless of whether the custom plugin works.

⚠️ Built-in ESLint rules vs. configurable ESLint rules: Code Analyzer bundles a SUBSET of ESLint rules in its base config. Rules like no-restricted-globals, no-restricted-syntax, and no-restricted-properties are core ESLint rules but are NOT active until you enable them in an eslint.config.js. They WILL appear in sf code-analyzer rules output ONLY after you configure them. Always validate (step 4) to confirm the rule actually loaded — do NOT assume a rule exists just because it's a core ESLint rule.

  1. Install the ESLint plugin (skip if using a built-in/core ESLint rule) — npm install --save-dev eslint-plugin-<name>
  2. Create/update eslint.config.js — add plugin and rule configuration (or just enable the built-in rule with "error" severity). The file MUST exist before configuring engines.eslint.eslint_config_file in code-analyzer.yml — Code Analyzer validates the path and fails if the file is missing.
  3. Configure Code Analyzer — set engines.eslint.eslint_config_file in code-analyzer.yml. Do this AFTER the file exists.
  4. Validatesf code-analyzer rules --rule-selector eslint:<ruleName>. If the rule does NOT appear, the config is wrong — do NOT proceed to testing. Check: (a) Is the eslint.config.js file in the correct location? (b) Does the plugin have meta.docs.description and meta.docs.url? (c) Is the rule deprecated? Code Analyzer silently excludes deprecated rules.
  5. Write a test sample file in the workspace (e.g., lwc/testSample/testSample.js) demonstrating the violation
  6. Test positivesf code-analyzer run --rule-selector eslint:<ruleName> --target <test-sample> — must find violations
  7. Test negative — run against existing clean LWC files — must find 0 violations
  8. Cleanup — delete ALL sample files AND their parent directories (LWC requires a folder per component). Use rm -rf <directory>, not just rm <file>. Do not leave empty directories or temporary test fixtures in the user's project.
  9. See references/eslint-custom-plugins.md for complete guide

Common ESLint patterns without a custom plugin

For no-restricted-globals, no-restricted-syntax, and no-restricted-properties config examples, see <skill_dir>/references/eslint-custom-plugins.md — "Banning APIs Without a Custom Plugin" section.

ESLint Discovery Examples

Example 1: Built-in rule (Tier 1)

  • User: "Ban console.log in LWC"
  • Discovery: sf code-analyzer rules --rule-selector eslint | grep console → finds no-console
  • Action: Enable no-console in eslint.config.js
  • Result: ✅ Done in 2 minutes (no custom plugin needed)

Example 2: Configurable rule (Tier 2)

  • User: "Ban setTimeout in LWC"
  • Discovery: No built-in no-setTimeout rule
  • Action: Use no-restricted-globals with custom message
  • Result: ✅ Done in 5 minutes (no custom plugin needed)

Example 3: LWC plugin rule (Tier 1)

  • User: "Ban innerHTML for XSS prevention"
  • Discovery: @lwc/lwc/no-inner-html already exists
  • Action: Enable @lwc/lwc/no-inner-html in config
  • Result: ✅ Done in 2 minutes (no custom plugin needed)

Example 4: Custom plugin justified (Tier 3)

  • User: "Flag imperative Apex calls without error handling"
  • Discovery: No built-in rule for this pattern
  • Analysis: Pattern requires checking import + .then() without .catch() — multi-node traversal
  • Action: Create custom plugin with visitor pattern
  • Result: ✅ Custom plugin is justified (20+ minutes effort)

See references/eslint-rules-discovery.md for complete discovery workflow.


Overriding Built-in Rule Thresholds

To customize severity or properties on existing rules without writing new ones:

  1. Create a custom ruleset referencing the built-in rule with overrides
  2. Add to configengines.pmd.custom_rulesets in code-analyzer.yml
  3. See references/advanced-pmd-patterns.md for override syntax and common examples

Multi-Rule Requests

When the user asks for multiple rules at once (e.g., "create 5 rules for AppExchange review"), follow this protocol:

Step 1: Plan the engine assignments FIRST

Before creating any rules, list ALL requested rules with their engine assignments. Present this plan to the user:

Rule plan:
1. RequireUserMode → PMD/XPath (needs test-class exclusion via AST)
2. NoHardcodedIds → Regex (text pattern, no structural context needed)
3. AuraEnabledCacheable → PMD/XPath (needs annotation + DML structural check)
4. CustomFieldDescription → PMD/XML (metadata governance)
5. NoSetTimeout → ESLint (JavaScript pattern)

Key decision factors for engine assignment:

  • Does the rule need to exclude test classes selectively? → PMD (XPath can check @Test = true())
  • Is it a pure text pattern with no structural context? → Regex
  • Does it need to understand code structure (annotations, nesting, DML)? → PMD
  • Is it JavaScript/LWC? → ESLint (never Regex)
  • Is it metadata XML? → PMD with language="xml"

Step 2: Create rules ONE AT A TIME, sequentially

Create each rule through its full workflow (create → validate → test positive → test negative → confirm working) before starting the next one. Do NOT batch-create rules — if one fails, it corrupts the config for all subsequent rules.

Order of creation:

  1. Regex rules first (fastest, fewest dependencies)
  2. PMD Apex rules (require ast-dump per rule)
  3. PMD XML rules (may share a single ruleset file)
  4. ESLint rules last (require npm/config setup)

Step 3: Multiple PMD rules can share ONE ruleset file

When creating multiple PMD rules, use the create-pmd-rule.js script for the first rule (it creates the ruleset XML file). For subsequent PMD rules going into the SAME ruleset file, you may add them manually to the existing XML file — this is the ONE exception to the "never edit manually" rule. The script always creates a new file; adding rules to an existing file requires editing the XML directly.

What NOT to do

  • ❌ Do NOT rewrite the entire code-analyzer.yml to reorganize it
  • ❌ Do NOT create all rules in parallel then validate all at once
  • ❌ Do NOT create a PMD rule with an unverified XPath from the reference docs — ast-dump each pattern
  • ❌ Do NOT mix engine types in one creation step (e.g., creating regex + PMD rules simultaneously)
  • ❌ Do NOT add engines.eslint.eslint_config_file to code-analyzer.yml before the file exists

High-Signal Rules

Rule Rationale
Rule names must match /^[A-Za-z@][A-Za-z_0-9@\-/]*$/ Code Analyzer validation rejects others
Regex must be /pattern/flags format JavaScript regex literal notation required
File extensions must start with . Validation enforces /^([.][a-zA-Z0-9-_]+)+$/
Always validate after creation sf code-analyzer rules --rule-selector <engine>:<name> catches config errors
Always test against sample code Catches XPath/regex mismatches before full scan
Use @FullMethodName for method calls in XPath More reliable than @Image or @MethodName alone
⚠️ NEVER skip ast-dump Run ast-dump, read output, THEN write XPath. No exceptions — even for "obvious" patterns. Using node names from memory without ast-dump verification is a skill failure.
PMD 7 boolean attributes: use = false() XPath function In PMD 7, boolean attributes like @WithSharing, @Abstract, @Final are ALWAYS present on the node (never absent). Compare with = false() (XPath boolean function), NOT string 'false'. @WithSharing = false() works. @WithSharing='false' (string) does NOT. not(@WithSharing) does NOT (attribute is always present).
code-analyzer.yml at project root Auto-discovered by CLI — placing it elsewhere causes silent failures for rule authors
XML rules MUST use local-name() Salesforce metadata namespace breaks bare element names
XML text matching MUST use @Text attribute text() does NOT work in PMD 7's XML language. Use //*[@Text='value'] to match text content, then navigate up with ../.. to reach parent elements.
@Text lives on CHILD text nodes, NOT on elements //*[local-name()='apiVersion'][@Text < 60] → WRONG (0 matches). //*[local-name()='apiVersion']/*[number(@Text) < 60] → CORRECT. The /* navigates to the child text node. For exact-match patterns, //*[@Text='value'] works because it searches ALL nodes including text nodes. But when you target a specific element by local-name() first, you must use /*[@Text...] to reach its text child.
XML rules need file_extensions config PMD only scans .xml by default — add file_extensions: { xml: [".xml"] } under engines.pmd. Do NOT add compound extensions (.permissionset-meta.xml) — the validator rejects them and .xml alone already covers all Salesforce metadata files.
Named capture groups (?<target>...) in regex Narrows the violation highlight to just the captured portion
Salesforce IDs start with 0, are exactly 15 or 18 chars Use /['"](?<target>0[a-zA-Z0-9]{14}(?:[a-zA-Z0-9]{3})?)['\"]/g — NOT {15,18} which also matches 16/17 char strings and produces false positives on normal words like 'BusinessAccount'
regex_ignore is per-LINE, not per-FILE It only skips lines where the ignore pattern matches. It does NOT exclude entire files or classes. A hardcoded ID on line 10 of a test class still flags unless that specific line contains the ignore pattern (e.g., @isTest).
ALWAYS use scripts to create rules Do NOT manually edit code-analyzer.yml for regex rules — quotes/backslashes in regex inside YAML cause parsing failures. The create-regex-rule.js script handles serialization correctly.
Override built-in rules via <rule ref="..."> Change thresholds without writing new rules
NEVER use Regex for JS/LWC/TS files Regex cannot distinguish code from comments/strings in JS — always use ESLint for JavaScript patterns
Check built-in ESLint rules before writing custom ones no-console, no-debugger, no-alert, eqeqeq, no-eval etc. already exist — just enable them in config

Gotchas

Issue Resolution
XPath written without running ast-dump ALWAYS run ast-dump first. Even if the rule works, skipping ast-dump is a process failure. Node names change between PMD versions and are not guessable.
SOQL-in-loop rule flags for (x : [SELECT...]) Use //ForEachStatement/BlockStatement//SoqlExpression (scope to body). The iterable SOQL is a direct child of ForEachStatement alongside BlockStatement — //ForEachStatement//SoqlExpression matches it as a false positive.
Used Regex for a JS/LWC/TS pattern NEVER use Regex for JavaScript files. Regex cannot distinguish code from comments/strings in JS. Always use ESLint — check if a built-in rule (e.g., no-console) already exists first.
XPath returns 0 matches (XML metadata) Three common causes: (1) Forgot local-name() — namespace blocks bare element names. (2) Used text()='value' — does NOT work in PMD 7. Use @Text='value' instead. (3) Put [@Text] predicate on the element — @Text lives on CHILD text nodes. Use /*[@Text...] to reach them.
Regex rule written inline into code-analyzer.yml — YAML parse error ALWAYS use create-regex-rule.js — quotes and backslashes inside YAML cause escaping failures. Never hand-write regex into the config file.
regex_ignore doesn't exclude test classes regex_ignore is per-LINE only. A SOQL query on line 50 of a test class flags because line 50 doesn't contain @isTest. For file-level exclusion: use ignores.files (global) or PMD XPath [not(ancestor::UserClass[ModifierNode[@Test = true()]])] (per-rule).
XPath @WithSharing='false' or not(@WithSharing) doesn't work PMD 7 boolean attributes (@WithSharing, @Abstract, @Final) are ALWAYS present. String ='false' doesn't match (it's a boolean). not(@attr) doesn't work (attribute is always present). Use the XPath boolean function: @WithSharing = false().
Loop-based rule only covers ForEachStatement Apex has 3 loop types: ForEachStatement, ForLoopStatement, WhileLoopStatement. All 3 must be in the XPath — omitting any one creates a silent coverage gap.
Rewrote code-analyzer.yml after script wrote it — YAML parse error NEVER manually rewrite the file after a script runs. Add top-level config blocks (like ignores.files) as new entries only — do NOT touch the engines.regex.custom_rules section the script generated.
Multiple rules created at once — one failure breaks all subsequent rules Create rules one at a time, sequentially. Complete the full workflow (create → validate → test) for each rule before starting the next.

For additional diagnostics (wrong severity, Java not found, ESLint config path, metadata file type scoping, etc.) see <skill_dir>/references/troubleshooting.md.


Cross-Skill Integration

Need Delegate to Reason
Run a full scan after creating rule dx-code-analyzer-run Scan execution and result presentation
Install Code Analyzer / fix prerequisites dx-code-analyzer-configure Setup and troubleshooting
Explain an existing built-in rule dx-code-analyzer-run Rule description and docs lookup
Edit code-analyzer.yml for engine settings dx-code-analyzer-configure Configuration management

Script Execution

<skill_dir> is the absolute path to the directory containing this SKILL.md file.

All scripts are bundled in the scripts/ subdirectory of the same directory that contains this SKILL.md file. Use the absolute path to that directory — do NOT use ./scripts/ as that resolves relative to the current working directory, not the skill directory.

node "<skill_dir>/scripts/create-regex-rule.js" \
  --name "RuleName" --regex "/pattern/flags" ...

⚠️ DO NOT:

  • ❌ Invent or generate script code yourself
  • ❌ Use bare relative paths like node scripts/create-regex-rule.js (won't resolve from user's CWD)
  • ❌ Use heredocs or inline script content
  • ❌ Skip resolving <skill_dir> — find the absolute path first

Reference File Index

File When to read
references/regex-rule-schema.md Building a regex rule — complete field reference, validation rules, multi-rule example
references/xpath-patterns.md Writing XPath for Apex — index of pattern categories with syntax reference and AST node vocabulary
references/xpath-patterns-governor-limits.md XPath patterns for SOQL/DML in loops, Database methods in loops
references/xpath-patterns-method-calls.md XPath patterns for banning methods and annotation patterns (@AuraEnabled, @future, @IsTest)
references/xpath-patterns-security.md XPath patterns for sharing declarations, SOQL security, hardcoded IDs
references/xpath-patterns-structure.md XPath patterns for code structure, test quality, naming conventions
references/apex-ast-reference.md Reading Apex AST dumps — node hierarchy, modifier attributes, key node types
references/metadata-xml-rules.md Writing rules for metadata XML — namespace workaround, common structures, XPath patterns
references/advanced-pmd-patterns.md Multi-rule rulesets, overriding built-in rules, exclusion patterns, Java rules, sharing across projects
references/eslint-rules-discovery.md READ FIRST for ANY ESLint request — discovery workflow, built-in rules, Tier 1-3 index
references/eslint-tier2-configurable.md ESLint Tier 2: no-restricted-globals, no-restricted-syntax, no-restricted-properties patterns
references/eslint-tier3-custom-plugins.md ESLint Tier 3: when to create custom plugins + complete examples for all tiers
references/eslint-custom-plugins.md Creating custom ESLint plugins — ONLY after discovery workflow confirms no built-in or configurable rule exists (Tier 3)
references/troubleshooting.md When validation or testing fails — error diagnosis by engine type
assets/pmd-ruleset-template.xml PMD XML skeleton with placeholders for the create-pmd-rule script
examples/regex-examples.md 6 real-world regex rules solving community-reported problems
examples/xpath-examples.md 6 real-world XPath rules for Apex with AST context and step-by-step creation
examples/metadata-xml-examples.md Index of 6 real-world metadata XML rules (permissions, descriptions, API versions, flows)
examples/metadata-xml-example-permissions.md Metadata examples: ModifyAllData/ViewAllData, field permissions in profiles
examples/metadata-xml-example-fields-api.md Metadata examples: custom field descriptions, minimum API version
examples/metadata-xml-example-flows.md Metadata examples: flow auto-layout, flow fault handlers
运行Salesforce代码分析器扫描安全、性能及风格违规。支持多引擎与目标,提供结果过滤、规则解释及自动修复功能。用于静态分析与代码质量检查,需通过指定脚本处理结果。
用户请求扫描代码或检查安全问题 提及具体引擎(如PMD, ESLint)或文件类型 询问规则含义或筛选分析结果 执行静态分析、代码审查或AppExchange审核
skills/dx-code-analyzer-run/SKILL.md
npx skills add forcedotcom/sf-skills --skill dx-code-analyzer-run -g -y
SKILL.md
Frontmatter
{
    "name": "dx-code-analyzer-run",
    "metadata": {
        "version": "1.0",
        "cliTools": [
            {
                "tool": [
                    "sf"
                ],
                "semver": ">=2.0.0"
            },
            {
                "tool": [
                    "node"
                ],
                "semver": ">=18.0.0"
            },
            {
                "tool": [
                    "git"
                ],
                "semver": ">=2.0.0"
            }
        ],
        "relatedSkills": [
            "dx-code-analyzer-configure",
            "dx-code-analyzer-custom-rule-create"
        ]
    },
    "description": "Run Salesforce Code Analyzer to scan code for security, performance, best practice, and code style violations. Supports all engines (PMD, ESLint, CPD, RetireJS, Flow, SFGE, ApexGuru), targets (files, folders, git diff), categories, and severities. Also handles post-scan exploration: filtering results by engine\/severity\/category\/file, and explaining what rules mean. TRIGGER when: user says 'scan my code', 'check security issues', 'run PMD\/ESLint', 'find duplicates', 'analyze Flows', 'check vulnerable libraries', 'AppExchange review', 'lint my LWC', 'static analysis', 'code quality', 'show security violations', 'what is this rule', 'explain ApexCRUDViolation', 'filter results', or mentions engines\/file types (.cls, .trigger, .js, .flow-meta.xml). Use this skill for scanning, exploring results, and listing rules. DO NOT TRIGGER when: user asks only about installation\/configuration (use dx-code-analyzer-configure), or wants to create a custom rule (use dx-code-analyzer-custom-rule-create)."
}

Running Code Analyzer Skill

CRITICAL: Mandatory Script Usage

Every interaction with Code Analyzer results MUST go through the bundled scripts in <skill_dir>/scripts/. No exceptions.

WRONG — never do this:

# WRONG: inline Python to parse results
python3 -c "import json; data = json.load(open('results.json'))..."

# WRONG: inline Node.js to parse results
node -e "const data = require('./results.json')..."

# WRONG: jq to filter results
cat results.json | jq '.violations[] | select(.engine=="pmd")'

# WRONG: reading the results file directly (it can be 10MB+)
Read tool → code-analyzer-results-*.json

Also forbidden: run_code_analyzer and any mcp__* tool — Bash only.

RIGHT — always do this:

# Summarize scan results
node "<skill_dir>/scripts/parse-results.js" "./code-analyzer-results-TIMESTAMP.json"

# Filter/rank/query results (by engine, severity, file, rule, category)
node "<skill_dir>/scripts/query-results.js" "./code-analyzer-results-TIMESTAMP.json" --engine pmd --summary

# List/browse available rules (by engine, category, language, severity)
node "<skill_dir>/scripts/list-rules.js" "Security" --top 10

# Look up what a rule means
node "<skill_dir>/scripts/describe-rule.js" "ApexCRUDViolation" --engine pmd

# Discover fixable violations
node "<skill_dir>/scripts/discover-fixes.js" "./code-analyzer-results-TIMESTAMP.json"

# Apply fixes (after user confirms)
node "<skill_dir>/scripts/apply-fixes.js" "./code-analyzer-results-TIMESTAMP.json"

# Summarize applied fixes
node "<skill_dir>/scripts/summarize-fixes.js" "./code-analyzer-results-TIMESTAMP.json"

# Filter vendor files (jQuery, Bootstrap, *.min.js) before applying fixes
node "<skill_dir>/scripts/filter-violations.js" "./code-analyzer-results-TIMESTAMP.json" "./code-analyzer-results-TIMESTAMP-filtered.json" --report

<skill_dir> is the absolute path to the directory containing this SKILL.md. Never use ./scripts/ — that resolves against the user's CWD, not the skill dir.

Any aggregation, filter, or rank question ("which file has the most violations?", "how many PMD issues?", "top rules by count", "break down by severity") is answered by query-results.js — its output already includes topRules, topFiles, and severityCounts.


Overview

Ecosystem: This skill is part of a 3-skill Code Analyzer suite — dx-code-analyzer-run (scans & results) · dx-code-analyzer-configure (setup, config, CI/CD) · dx-code-analyzer-custom-rule-create (custom rule authoring).

This skill translates natural-language requests ("scan for security issues", "check my changes") into the correct sf code-analyzer run command, executes scans across any combination of engines/targets/severities, and presents actionable results. When engine-provided fixes are available, it discovers them, asks for user confirmation, applies them safely, and offers verification. Use it for static analysis, security reviews, AppExchange certification, code-quality checks, and finding duplicates/vulnerabilities in Salesforce projects.

In scope: running scans, parsing/filtering/ranking results, applying engine auto-fixes, diff-based scans, all output formats (JSON/HTML/SARIF/CSV/XML), describing/listing rules, scan-failure troubleshooting.

Out of scope: installing/configuring sf or the plugin (→ dx-code-analyzer-configure), writing custom rules/engines (→ dx-code-analyzer-custom-rule-create), AI-generated fixes beyond engine-provided ones, deep refactoring, CI/CD setup (→ dx-code-analyzer-configure).

Allowed tools: Bash (sf code-analyzer, node, git diff, date), Read, Write, Edit. Forbidden: any MCP tool, Agent tool, web tools, other skills, Python, jq, inline scripts/heredocs. This skill owns the complete scan-fix-verify-query-explain workflow end-to-end.


Command Syntax Rules (READ FIRST — ABSOLUTE)

  1. The command is sf code-analyzer run — NOT sf scanner run (deprecated v3).
  2. No --format flag. Use --output-file <path>.<ext>; the extension determines the format.
  3. Always pass --output-file with a timestamped name (e.g., ./code-analyzer-results-20260512-143022.json) — do not rely on stdout.
  4. Foreground only (no run_in_background); timeout 1200000ms for large scans.
  5. Invalid v3 flags that cause errors: --format, --engine, --category, --json. Use --rule-selector + --output-file instead.
  6. Tool restriction: Bash, Read, Write, Edit only. No MCP tools, no Agent tool, no web tools, no other skills.

Why: the v4+ CLI redesigned the flag interface; v3 flags now error.

Full flag/selector docs: <skill_dir>/references/flag-reference.md.


Prerequisites

User needs: Salesforce CLI (sf), @salesforce/plugin-code-analyzer (v5.x+), Java 11+ (PMD/CPD/SFGE), Node.js 18+ (ESLint/RetireJS), Python 3 (Flow), authenticated org (ApexGuru).

Pre-flight: run sf code-analyzer --help 2>&1 | head -1. If that fails, or if a scan reports an engine startup error (e.g., "PMD failed to start", "java: command not found", "SFGE failed"):

  1. Stop — do not attempt to install/diagnose prerequisites yourself.
  2. Delegate to dx-code-analyzer-configure — it handles all setup.
  3. After it finishes, return here and re-run the scan.

If a scan fails for other reasons, see <skill_dir>/references/error-handling.md.


Quick Start: Common Patterns

Match the request below; if it matches, jump to Step 3 (Build Command). Otherwise, walk Step 1.

User Says Rule Selector Notes
"scan my code" / "run code analyzer" Recommended Curated set, all file types
"check for security issues" / "security review" all:Security:(1,2) All engines, Critical+High
"scan my changes" / "check the diff" (see Step 1.5) Get files via git diff, filter to scannable types, pass via --target
"run PMD" / "check my Apex" pmd Apex classes and triggers
"lint my LWC" / "check my JavaScript" eslint JavaScript/TypeScript/LWC
"find duplicates" / "check for copy-paste" cpd Code clones
"check for vulnerabilities" / "scan libraries" retire-js JavaScript library CVEs
"deep analysis" / "data flow analysis" sfge Java 11+, 10–20 min, use --workspace "force-app"
"performance analysis" / "governor limits" apexguru Authenticated org required
"analyze my Flows" flow --target **/*.flow-meta.xml, Python 3
"AppExchange security review" all:Security:(1,2) See <skill_dir>/references/special-behaviors.md → AppExchange

Step 1: Parse the User's Intent

Analyze the request along these 7 dimensions; any can combine.

1.1 ENGINE

PMD/Apex → pmd · ESLint/JS/TS/lint → eslint · Flows → flow · duplicates/CPD → cpd · vulnerabilities/CVE/RetireJS → retire-js · SFGE/data flow → sfge · performance/ApexGuru → apexguru · regex → regex · everything → all · unspecified → Recommended.

1.2 CATEGORY

security/OWASP → Security · performance → Performance · best practices → BestPractices · style/format → CodeStyle · design/complexity → Design · bugs → ErrorProne · docs → Documentation.

1.3 SEVERITY

1=Critical · 2=High · 3=Moderate · 4=Low · 5=Info. "critical only" → 1 · "critical+high" → (1,2) · "moderate and above" → (1,2,3).

1.4 SPECIFIC RULE

If the user names a rule (e.g., "ApexCRUDViolation", "no-unused-vars"): --rule-selector <engine>:<ruleName>, or just <ruleName> if engine is ambiguous.

⚠️ Partial names: --rule-selector requires the exact full rule name (e.g., @salesforce-ux/slds/no-hardcoded-values-slds2, not no-hardcoded-values). No wildcards. If you are not 100% certain, look it up first — do not guess:

sf code-analyzer rules --rule-selector all 2>&1 | grep -i "USER_KEYWORD"

Multiple matches → ask the user which. Zero matches → tell the user nothing matched.

1.5 TARGET

specific path → --target <path> · glob ("all Apex") → --target **/*.cls,**/*.trigger · "my changes"/"diff" → git diff --name-only [base]...HEAD, filter to scannable types, pass as --target · "LWC" → --target **/lwc/** · "Flows" → --target **/*.flow-meta.xml · unspecified → omit (entire workspace).

Diff-filtering details: <skill_dir>/references/special-behaviors.md.

1.6 OUTPUT

Default JSON. Only change if the user explicitly asks. Name: ./code-analyzer-results-<YYYYMMDD-HHmmss>.<ext> via TIMESTAMP=$(date +%Y%m%d-%H%M%S). Formats: .json (default), .html, .sarif, .csv, .xml.

1.7 COMPARISON / DELTA

"new since main" → git diff --name-only main...HEAD → scan those · "since last commit" → HEAD~1 · "vs develop" → develop...HEAD.


Step 2: Build the Rule Selector

Syntax: : = AND, , = OR, () = grouping.

  • Engine only: pmd
  • Engine + category: pmd:Security
  • Engine + severity: pmd:2
  • Complex: (pmd,eslint):Security:(1,2) = (PMD or ESLint) AND Security AND sev (1 or 2)
  • Specific rule: pmd:ApexCRUDViolation
  • All: all

More: <skill_dir>/references/command-examples.md.


Step 3: Build the Full Command

TIMESTAMP=$(date +%Y%m%d-%H%M%S)
sf code-analyzer run \
  --rule-selector <selector> \
  --target <targets> \                                              # optional
  --output-file "./code-analyzer-results-${TIMESTAMP}.json" \       # default JSON
  --include-fixes \                                                 # always
  --workspace <path>                                                # optional
  • Default to timestamped JSON; only change format on explicit request.
  • Always pass --include-fixes (enables Step 6 auto-fix).
  • Omit --target to scan the whole workspace.
  • Diff scans: git diff --name-only → filter scannable types → pass as --target.

Special cases (SFGE/ApexGuru/AppExchange/diff): <skill_dir>/references/special-behaviors.md.


Step 4: Execute the Scan

Use the Bash tool only — never the run_code_analyzer MCP tool.

  1. Generate the timestamp via Bash: date +%Y%m%d-%H%M%S → e.g. 20260512-143022.
  2. Tell the user:
    Starting scan...
    Results: ./code-analyzer-results-20260512-143022.json
    Log:     ./code-analyzer-results-20260512-143022.log
    May take several minutes for large codebases.
    
  3. Run with the literal timestamp baked in (not $TIMESTAMP), foreground, timeout 1200000ms, tee to a .log:
    sf code-analyzer run --rule-selector Recommended \
      --output-file "./code-analyzer-results-20260512-143022.json" \
      --include-fixes 2>&1 | tee "./code-analyzer-results-20260512-143022.log"
    
  4. Exit 0 = success. On error, read both the log file and <skill_dir>/references/error-handling.md.
  5. Immediately parse results (Step 5) — do not ask the user what to do next.

Step 5: Parse and Present Results

Run the parse script straight after the scan — do not pause to ask:

node "<skill_dir>/scripts/parse-results.js" "./code-analyzer-results-TIMESTAMP.json"

⚠️ DO NOT:

  • ❌ Invent or generate script code yourself
  • ❌ Use bare relative paths like node scripts/parse-results.js (won't resolve from user's CWD)
  • ❌ Use heredocs or inline script content
  • ❌ Use jq as a substitute for the parse script (shell quoting will break)
  • ❌ Read the JSON file directly

Presentation template

## Scan Complete

**Found X violations** across Y files.

| Severity | Count |
|----------|-------|
| Critical (1) | X |
| High (2) | X |
| Moderate (3) | X |
| Low (4) | X |
| Info (5) | X |

### Top Issues
| # | Rule | Engine | Sev | File | Line |
|---|------|--------|-----|------|------|
| 1 | ApexCRUDViolation | pmd | 2 | AccountService.cls | 42 |
| ... up to 10 most critical |

### Top Rules by Frequency
| Rule | Engine | Count |
|------|--------|-------|
| no-var | eslint | 170 |
| ... |

Full results: `./code-analyzer-results-20260512-143022.json`

Scale to result size: 0 → "no violations found"; 1–10 → all in one table; 11–50 → severity counts + top 10; 50–5000 → counts + top 10 violations + top 10 rules + top 5 files; 5000+ → same, plus suggest narrowing scope (severity/category/folder). Always end with the output path and offer next actions: filter / explain rule / apply fixes.

Large-result handling: <skill_dir>/references/special-behaviors.md.


Step 6: Apply Engine-Provided Fixes (Post-Scan)

Engine-provided fixes are deterministic (not AI-generated). Flow: vendor filter (if needed) → discover → present → wait for user confirmation → apply → summarize.

6.1 Vendor file filter (when needed)

Run if the user said "fix my code" / "project source", or if top-violation files are vendor libs (jQuery, Bootstrap, *.min.js):

node "<skill_dir>/scripts/filter-violations.js" \
  "./code-analyzer-results-TIMESTAMP.json" \
  "./code-analyzer-results-TIMESTAMP-filtered.json" \
  --report

Report: "Excluded X vendor files (Y violations) — jQuery, Bootstrap, etc. Applying fixes to Z project files only." Use the filtered file in 6.2+. Detection logic: <skill_dir>/references/vendor-file-handling.md.

6.2 Discover

node "<skill_dir>/scripts/discover-fixes.js" "./code-analyzer-results-TIMESTAMP.json"

6.3 Present + ASK (then STOP)

### Engine-Provided Fixes Available
**X of Y violations** have auto-fixes provided by the analysis engine:

| Rule | Engine | Sev | Fixable Count |
|------|--------|-----|---------------|
| no-var | eslint | 3 | 170 |
| ... |

These are safe, deterministic fixes generated by the engines (not AI-generated).

Would you like me to apply these fixes? (yes / no / select specific rules)

⚠️ Stop and wait for the user's reply, even if they originally said "scan and fix everything". Apply only on a fresh "yes" / "apply" / "go ahead" in the next turn.

6.4 Apply

node "<skill_dir>/scripts/apply-fixes.js" "./code-analyzer-results-TIMESTAMP.json"

(Filtered file if 6.1 created one.)

6.5 Summarize (MANDATORY immediately after 6.4)

node "<skill_dir>/scripts/summarize-fixes.js" "./code-analyzer-results-TIMESTAMP.json"

Then present:

### Engine-Provided Fixes Applied Successfully ✓
**Applied X auto-fixes across Y files.**

| Severity | Fixes Applied |
|----------|---------------|
| Critical (1) | X |
| ... |

| Rule | Fixes Applied |
|------|---------------|
| no-var | 169 |
| ... |

Want me to re-run the scan to verify the fixes resolved the violations?

6.6 — Handling the user's choice

  • Decline / "no": skip apply, skip summarize. Do not re-scan.
  • "Select rules": filter the discovery list to those rules and pass the filtered file to apply-fixes.js.
  • "All" / "yes": run apply-fixes.js against the full (or vendor-filtered) results file as-is.

6.7 — Optional re-scan for verification

If the user accepts the offer in 6.5, re-run the same scan with a new timestamp (do not overwrite the original). Compare violation counts before vs. after and show the delta — fixes that resolved cleanly will drop out; remaining violations either need manual remediation or are unrelated.


Step 7: Query and Filter Existing Results

After Step 5, the user may want to drill into specific subsets without re-running the entire scan. This step handles all result-exploration requests.

When to trigger

Activate when the user asks to slice, filter, rank, or explore existing results:

  • "Show me just the security violations"
  • "What's in AccountService.cls?"
  • "Show only PMD issues" / "Filter to critical and high"
  • "What ESLint rules fired?" / "Show violations in the lwc folder"
  • "Top 20 most severe" / "Which file has the most violations?"
  • "What are the most common rules?" / "How many violations per engine?" / "Break it down by severity"

Important: Any question about existing scan results — filtering, ranking, counting, aggregating — MUST use query-results.js. NEVER write inline Python, jq, or ad-hoc scripts to parse the results JSON. The query script already provides topRules, topFiles, and severityCounts in its output.

How to execute

Run the query script against the same results file from Step 4 (no re-scan needed):

node "<skill_dir>/scripts/query-results.js" "./code-analyzer-results-TIMESTAMP.json" [options]
User says Options
"security violations" --category Security
"PMD issues only" --engine pmd
"critical and high" / "sev 1-2" --severity 1,2
"in AccountService.cls" --file AccountService.cls
"the ApexCRUDViolation rule" --rule ApexCRUDViolation
"top 20" --top 20
"sort by file" --sort file
"just give me counts" --summary
"which file has the most violations?" --sort file --summary (read topFiles)
"which file has most PMD violations?" --engine pmd --summary (read topFiles)
"most common rules?" --summary (read topRules)
"how many per engine?" use Step 5's summary, or run with --engine X --summary per engine
Combinations --engine pmd --severity 1,2 --top 5

Output format and presentation templates: <skill_dir>/references/post-scan-workflows.md.


Step 8: Describe a Rule

When the user asks "what does this rule mean?" or "how do I fix this?", use this step to look up and explain a specific rule.

When to trigger

  • "What is ApexCRUDViolation?"
  • "Explain this rule" / "Why is this flagged?"
  • "What does no-var mean?"
  • "How do I fix OperationWithLimitsInLoop?"
  • "Tell me about this violation"

How to execute

node "<skill_dir>/scripts/describe-rule.js" "<rule-name>" [--engine <engine>]

Pass --engine when known (from scan context); omit for a broader search. Returns one of success / multiple_matches / not_found. Status handling and templates: <skill_dir>/references/post-scan-workflows.md.


Step 9: List Available Rules

Triggers: "what security rules are available?", "list all PMD rules", "rules for JavaScript", "Recommended rules", "how many ESLint rules?", "rules for Apex".

node "<skill_dir>/scripts/list-rules.js" "<selector>" [options]
User says Selector Options
"security rules" Security
"PMD rules" pmd
"ESLint security rules" eslint:Security
"JavaScript rules" JavaScript
"Apex rules" Apex
"Recommended rules" Recommended
"high severity rules" (1,2)
"just give me counts" Recommended --count-only
"top 10 security rules" Security --top 10

Filters: --engine, --severity, --top (default 100), --count-only. The script pre-validates selector tokens (catches typos like secruity) before calling the CLI. Presentation: <skill_dir>/references/post-scan-workflows.md.


Cross-Skill Integration

This skill is part of a 3-skill Code Analyzer ecosystem. Hand off cleanly rather than attempting work that belongs to another skill.

When THIS skill delegates to dx-code-analyzer-configure:

  • Pre-flight check fails (CLI missing, plugin not installed, engine prereqs broken) → stop, delegate, return here after fix
  • User asks to set up CI/CD, edit code-analyzer.yml, change severities, or disable engines → delegate entirely

When THIS skill delegates to dx-code-analyzer-custom-rule-create:

  • User asks to create a new rule, write XPath, write a regex rule, or enforce a pattern not covered by built-in rules → delegate entirely. Do NOT attempt to create rules here.

When other skills hand off HERE:

  • dx-code-analyzer-configure completes setup → proceed with scan (Step 1–5)
  • dx-code-analyzer-custom-rule-create finishes creating a rule → proceed with scan targeting the new rule (e.g., --rule-selector pmd:<RuleName>) to verify it works

Ownership boundary

This skill owns the complete scan → explore → fix workflow end-to-end. It does NOT own installation, config file management, or rule authoring.


Constraints & Gotchas

Item Why / Fix
Use timestamped JSON + .log via tee Prevents overwrite; matches log to results
--format flag Removed in v4+; use --output-file <path>.<ext>
Foreground, 1200000ms timeout SFGE can take 10–20 min; backgrounding loses output
Run scripts with absolute <skill_dir> path ./scripts/ resolves against the user's CWD, not the skill dir
Never apply fixes without confirmation User must approve code modifications
Vendor file check before fixes If 50%+ vendor (jQuery/Bootstrap/*.min.js), filter first
Fix-script order: filter (if needed) → discover → apply → summarize Skipping summary leaves the user without an outcome report
SFGE needs explicit --workspace Otherwise template files cause compilation errors
Look up partial rule names first Guessing returns 0 results; use sf code-analyzer rules
ONLY Bash tool, never MCP run_code_analyzer and other MCP tools bypass the script workflow
Never invoke other skills for fixes This skill owns the full workflow end-to-end
Query existing results, don't re-scan Step 7 filters existing JSON instantly
Scan returns 0 results Invalid rule selector — verify with sf code-analyzer rules --rule-selector <selector>
jq parsing fails Shell quoting — use parse-results.js / query-results.js instead
Inline scripts written by LLM Never write scripts — use existing ones in <skill_dir>/scripts/
Ranking/aggregation answered by ad-hoc Python Always use query-results.js; output already has topFiles/topRules/severityCounts

Reference File Index

Scripts (always execute via node with the absolute <skill_dir>/ prefix, never Read):

File When to use
<skill_dir>/scripts/parse-results.js Step 5 — extract summary from scan JSON
<skill_dir>/scripts/filter-violations.js Step 6.1 — exclude vendor files (jQuery, Bootstrap) from fixes
<skill_dir>/scripts/discover-fixes.js Step 6.2 — identify fixable violations
<skill_dir>/scripts/apply-fixes.js Step 6.4 — apply engine fixes after user confirms
<skill_dir>/scripts/summarize-fixes.js Step 6.5 — summarize applied changes
<skill_dir>/scripts/query-results.js Step 7 — filter/drill into existing results without re-scanning
<skill_dir>/scripts/describe-rule.js Step 8 — look up rule description and documentation
<skill_dir>/scripts/list-rules.js Step 9 — list/browse available rules by selector with validation

References (read on demand):

File When to read
references/quick-start.md Command-syntax templates
references/flag-reference.md Full flag docs, rule-selector syntax
references/error-handling.md Scan-failure diagnosis
references/engine-reference.md Engine capabilities, file types, rule tags
references/command-examples.md Less-common command scenarios
references/special-behaviors.md SFGE/ApexGuru/AppExchange/diff/large scans
references/vendor-file-handling.md Vendor-file detection and filtering
references/post-scan-workflows.md Steps 7–9 — querying, rule description, rule listing

examples/ contains output-structure validation and command patterns (basic/large/security scans, fix workflows).

分析DevOps测试失败和代码违规,用通俗语言解释根因、涉及文件及修复方向。仅在用户明确要求时创建修复工作项。适用于解释质量门禁失败或翻译违规信息,不用于直接生成代码。
用户询问运行失败的根因 需要解释质量门禁失败 用户分享失败载荷并寻求解决建议 用户要求创建修复工作项
skills/dx-devops-test-failures-analyze/SKILL.md
npx skills add forcedotcom/sf-skills --skill dx-devops-test-failures-analyze -g -y
SKILL.md
Frontmatter
{
    "name": "dx-devops-test-failures-analyze",
    "metadata": {
        "version": "1.0",
        "minApiVersion": "67.0"
    },
    "description": "Analyzes DevOps Center test failures and Code Analyzer violations in plain language — failure category, offending file\/class\/method\/line, rule violated, fix direction, and prioritized improvement suggestions (test-code vs production-code) — then optionally creates a tracked fix WorkItem on explicit request. Analysis is pure reasoning; work-item creation is a confirmation-gated write. Use this skill to explain failures or improvement suggestions, translate Code Analyzer violations, or track a fix as a work item. TRIGGER when: a run failed and the user wants root cause; a quality gate failure needs explaining; violations need translating; the user shares a failure payload and asks how to address it; wants to strengthen tests; or wants to create a fix work item, log a remediation, or assign a failure. DO NOT TRIGGER when: the user wants fix code written (use platform-apex-generate) or new test classes authored (use platform-apex-test-generate)."
}

Analyze DevOps Center Test Failures

Parses a test failure or Code Analyzer violation payload, explains it in plain language, produces prioritized improvement suggestions, and — only on explicit user request — creates a tracked fix work item. Parts 1–2 are pure reasoning (no writes); Part 3 is an optional, confirmation-gated write.

Never expose raw JSON, stack traces, or internal Salesforce error codes to the user. Always translate to file name, method, line, and plain description.


Prerequisites

  • Parts 1–2 (analysis): If the failure payload is already in context, no prerequisites are needed — this is pure reasoning. If you must fetch the payload yourself, run prerequisites (references/prerequisite-checks.md, Prereqs 1–4) and obtain the execution result via dx-devops-test-suite-run (its polling step).
  • Part 3 (work item): Run Prerequisites 1–4. You also need a DevopsProjectId to file under and an OwnerId (assignee). See references/work-item-creation.md.

Part 1 — Classify and explain each failure

Determine the failure category, then for each failure extract and translate to plain language: offending file/class, method, line number, the rule or assertion violated, and a fix direction (without writing code). Group failures by category if more than one.

Category Description
Assertion failure A test assertion failed (expected vs actual mismatch)
Exception An unhandled exception was thrown
Code Analyzer violation A static-analysis rule was violated (e.g. ApexCRUDViolation)
Timeout Test exceeded execution time limit
Compile error Class failed to compile

Output format:

Test failure summary:

<N> failure(s) found:

1. [<Category>] `<ClassName>.cls` — `<methodName>()` at line <N>
   What happened: <plain-language description>
   Rule violated: <ruleName or assertion description>
   Fix direction: <plain-language suggestion>

Full category/pattern tables and Code Analyzer rule translations: references/failure-categories.md and references/code-analyzer-violations.md.

Empty / no-data case: If the payload contains no failures or violations, report that clearly (e.g. "No failures found in the provided execution results.") and stop. Do NOT fabricate failures or suggestions.


Part 2 — Improvement suggestions

Run this after execution completes with failures, not on static source. For each failed test, reason over the failure message (the primary signal) to identify what the test is not handling, then produce a specific, actionable suggestion and a fix location (Test vs Production code). The full failure-pattern → suggestion mapping is in references/failure-categories.md.

Test improvement suggestions based on execution results:

`<testMethodName>()` — [Assertion Failure / Exception / etc.]
Failure: "<failure message>"
What this reveals: <plain-language explanation>
Suggestion: <specific, actionable recommendation>
Fix location: Test | Production code

Overall: <N> improvement(s) across <M> failed test(s).

Do not rewrite the test — only describe what needs to change and why. Fix location: Production code indicates a code defect exposed by a sound test (track separately, not a test-quality blocker). Fix location: Test indicates the test needs hardening (setup, assertions, edge cases).


Part 3 — Create a fix work item (optional, on request only)

Trigger only when the user wants to create a fix work item, log a remediation, or assign a failure to a developer. This is a write operation with a mandatory confirmation gate. Follow references/work-item-creation.md for inputs, the subject/assignee/project confirmation gate, the sf data create record --sobject WorkItem call, and error handling.

Use WorkItem (no namespace) — DevopsWorkItem is not a supported sObject in this org version.

If no DevopsProject exists in the org, report that the work item cannot be created until a project is set up — do NOT fabricate a project or proceed.


Related skills

  • dx-devops-test-suite-run — produces the failure payload (via its polling step) that feeds this skill.
  • dx-devops-test-suite-assignments-configure — assign/strengthen the suites whose tests are failing.
  • platform-apex-generate / platform-apex-test-generate — to actually write fix code or new test classes (out of scope here).
配置DevOps Center管道测试基础设施,支持启用/设置新测试提供者、同步已配置提供者的套件列表,或在阶段上配置质量门限。需先运行前置检查并获用户确认,避免误配或重复记录。
用户希望启用、设置或添加尚未配置的测试提供者 用户希望重新同步或刷新已配置提供者的套件列表以获取新套件 用户希望在管道阶段上设置或配置质量门限、覆盖率阈值或测试基准
skills/dx-devops-test-pipeline-configure/SKILL.md
npx skills add forcedotcom/sf-skills --skill dx-devops-test-pipeline-configure -g -y
SKILL.md
Frontmatter
{
    "name": "dx-devops-test-pipeline-configure",
    "metadata": {
        "version": "1.0",
        "minApiVersion": "67.0"
    },
    "description": "Configures DevOps Center pipeline testing infrastructure: enables a test provider so its suites become available, re-syncs a configured provider to pull in new suites, or creates a quality gate with rules on a stage. Routes by intent across three modes after running shared prerequisite checks and an explicit confirmation gate. Use this skill when a user wants to set up, configure, enable, sync, or refresh a test provider, or set\/configure a quality gate or coverage threshold on a DevOps Center pipeline stage. TRIGGER when: the user wants to configure\/enable\/add\/set up a test provider, re-sync or refresh a provider's suite list, pull in new suites, or set\/configure a quality gate, coverage threshold, or testing benchmark on a stage. DO NOT TRIGGER when: assigning existing suites to a stage (use dx-devops-test-suite-assignments-configure), running or retriggering a suite (use dx-devops-test-suite-run), or non-DevOps-Center work."
}

Configure DevOps Center Pipeline Testing Infrastructure

Sets up and configures a DevOps Center pipeline's testing infrastructure. This skill handles three closely related "configure your pipeline" operations that share the same org context, prerequisites, and entity scope (the pipeline level). Pick the mode that matches the user's intent.

API version: All DevOps testing system calls target Salesforce API v67.0 (minimum required).

Important: All DevOps Center data (pipelines, stages, providers, suites, gates) lives in the Salesforce org — NOT the local repo. Never search the filesystem for pipeline configuration. Always query the org with sf data query or sf api request rest.


Step 1 — Run prerequisites first (always)

Before any query or system call, run the prerequisite checks in references/prerequisite-checks.md. On any failure, surface the plain-language message and stop — never write to an unverified environment.

  • Modes A & B (provider configure/sync): run Prerequisites 1–4 (org login, Agentforce DX plugin, DevOps Center org auth, pipeline identified). Prerequisite 5 (stage) is not required — providers are configured at the pipeline level.
  • Mode C (quality gate): run Prerequisites 1–4 and Prerequisite 5 (stage). Prereq 5 gives the DevopsPipelineStage only — the target DevopsTestSuiteStage record Id is resolved separately in Mode C's Step 0 (trigger → suite-stage row).

Carry forward the resolved doce-org-alias, pipelineId, and (Mode C) stageId / testSuiteStageId.


Step 2 — Select the mode

If the user wants to… Mode Follow
Enable / set up / add a provider that is not yet configured A — Configure a test provider references/configuring-test-provider.md
Re-sync / refresh an already-configured provider to pull in new suites B — Sync a configured provider references/syncing-test-providers.md
Set / configure a quality gate, coverage threshold, or testing benchmark on a stage C — Configure a quality gate references/configuring-quality-gate.md

Disambiguating A vs B (the critical decision): First fetch the pipeline's providers (GET .../testProviders?status=all) — both modes start there. Then:

  • Provider is Available (not configured) → Mode A (configure).
  • Provider is Configured but suites are stale/missing → Mode B (sync).
  • Provider is Configured and the user can't see suites when assigning to a stage → this is a stage-assignment gap, not a configuration gap. Redirect to dx-devops-test-suite-assignments-configure.

Never POST to the configure endpoint for an already-configured provider — it creates duplicate DevopsPipelineTestProvider records. See references/gotchas.md.


Step 3 — Confirmation gate (required in every mode)

Every mode mutates org state and must show a confirmation gate before any write. Each mode's reference file contains its exact gate wording (Mode C additionally requires a mandatory impact preview before the gate). Do not call any write API until the user gives an affirmative response. If the user declines, stop without writing.


Step 4 — Execute and report

Follow the chosen reference file for the exact API calls, success messages, and error handling:

  • references/configuring-test-provider.md — Mode A
  • references/syncing-test-providers.md — Mode B
  • references/configuring-quality-gate.md — Mode C
  • references/error-handling.md — consolidated status-code → plain-language tables for all modes
  • references/gotchas.md — duplicate-provider trap, API-name differences, trigger-type rules

Never expose raw API errors, stack traces, or JSON payloads to the user — always translate to plain language.


Related skills

  • dx-devops-test-suite-assignments-configure — after configuring/syncing a provider, assign or map its suites to a stage; also recommends which suites to run for a commit.
  • dx-devops-test-suite-run — run a suite, or retrigger a quality gate after fixes meet the threshold.
  • dx-devops-test-failures-analyze — explain failures from a run and optionally create a fix work item.
用于推荐和配置DevOps Center测试套件分配。Mode A基于代码差异推荐套件并识别覆盖缺口;Modes B-D通过API将套件映射到管道阶段或管理测试类,包含强制影响预览与确认机制,确保操作合规。
用户询问针对特定提交应运行哪些测试套件 用户希望将未链接的测试套件分配到管道阶段 用户需要批量映射多个套件或增删套件内的测试类
skills/dx-devops-test-suite-assignments-configure/SKILL.md
npx skills add forcedotcom/sf-skills --skill dx-devops-test-suite-assignments-configure -g -y
SKILL.md
Frontmatter
{
    "name": "dx-devops-test-suite-assignments-configure",
    "metadata": {
        "version": "1.0",
        "minApiVersion": "67.0"
    },
    "description": "Recommends and manages DevOps Center test suite assignments for pipeline stages. Mode A analyzes a commit diff against assigned suite metadata to recommend relevant existing suites and flag coverage gaps (pure reasoning). Modes B-D assign a single suite, bulk-map multiple suites with a mandatory impact preview, or add\/remove test classes with governance rules, via the testSuiteStages Connect API. Use this skill to recommend suites for a commit, assign or map suites to stages, or add\/remove tests in a suite. TRIGGER when: the user asks which suites to run for a commit\/diff or what covers their changes; a suite is unlinked and the user wants it assigned; the user wants to configure suite-to-stage mappings, assign multiple suites, or add\/remove\/sync tests in a suite. DO NOT TRIGGER when: configuring or syncing a test provider (use dx-devops-test-pipeline-configure), running suites (use dx-devops-test-suite-run), or authoring\/running tests directly (use platform-apex-test-generate or platform-apex-test-run)."
}

Configure DevOps Center Suite Assignments

Recommends which existing test suites to run for a change, and manages how suites are assigned and mapped to pipeline stages. These operations share the same suite-stage metadata: a recommendation surfaces relevant suites and flags gaps, and those gaps lead directly into assignment.

API version: All DevOps testing system calls target Salesforce API v67.0 (minimum required).

Important: All DevOps Center data lives in the Salesforce org — NOT the local repo. Never search the filesystem for suite configuration. Always query the org with sf data query or sf api request rest.


Step 1 — Run prerequisites first (always)

Run the prerequisite checks in references/prerequisite-checks.md before any query or system call. On any failure, surface the plain-language message and stop.

  • Mode A (recommend): Prerequisites 1–4 (org login, plugin, DevOps Center org auth, pipeline identified). No stage required — recommendation reads the pipeline-level Review trigger.
  • Modes B–D (assign/map/classes): Prerequisites 1–4 and Prerequisite 5 (stage). You need doce-org-alias, pipelineId, and stageId.

Step 2 — Select the mode

If the user wants to… Mode Follow
Know which suites to run for a commit/diff, or what covers their changes A — Recommend suites references/recommendation-logic.md
Assign one suite to a stage as a one-off B — Assign a single suite references/suite-assignment-modes.md
Bulk-map multiple suites to a stage as a testing strategy C — Map multiple suites references/suite-assignment-modes.md
Add/remove individual test classes within a suite assignment D — Add/remove classes references/suite-assignment-modes.md

Mode A is pure reasoning (no writes). Modes B–D mutate org state via the same testSuiteStages endpoint (references/api-endpoint.md).

How A feeds B–D: When recommendation flags a relevant suite that is not assigned to the stage, that gap is the input to Mode B/C. When it flags a new method with no suite coverage, direct the developer to author tests manually (v1 constraint — never suggest generating tests here).


Step 3 — Governance & confirmation (Modes B–D)

Modes B–D must confirm before any write:

  • Mode B — single confirmation prompt naming the suite, stage, and event.
  • Mode C — a mandatory impact-preview table (Suite / Stage / Event / Action) before the confirmation gate.
  • Mode D — re-present the final test list before confirming. Rejected tests must be EXCLUDED from the payload. If tests were modified during review, re-present the final list before requesting confirmation. Never call without explicit approval.

Do not call the API until the user gives an affirmative response. If the user declines, stop without writing. Full wording for each gate is in references/suite-assignment-modes.md.

Mode A (recommendation) makes no writes and needs no confirmation gate.


Step 4 — Execute and report

Follow the chosen reference file:

  • references/recommendation-logic.md — Mode A: diff classification, provider matching, ranking, gap flagging, output format.
  • references/suite-assignment-modes.md — Modes B–D: inputs, confirmation wording, success messages.
  • references/api-endpoint.md — the shared testSuiteStages POST payload schema (Modes B–D).
  • references/error-handling.md — status-code → plain-language tables.

Never expose raw API errors, stack traces, or JSON to the user.


Related skills

  • dx-devops-test-pipeline-configure — if the suite you want to assign doesn't appear yet, configure or re-sync the provider; also configures quality gates on a stage after mapping.
  • dx-devops-test-suite-run — execute a recommended/assigned suite on a stage.
  • dx-devops-test-failures-analyze — analyze failures and improvement suggestions for the tests within a suite.
在DevOps Center管道阶段触发测试套件执行并轮询至完成。需用户确认后调用API,提取runId后自动轮询结果或失败分析,支持重试质量门禁,不用于直接运行Apex测试或配置新门禁。
用户希望在管道阶段启动或运行测试套件 用户需要重新运行修复后的质量门禁 用户希望监控进行中的测试运行直至完成
skills/dx-devops-test-suite-run/SKILL.md
npx skills add forcedotcom/sf-skills --skill dx-devops-test-suite-run -g -y
SKILL.md
Frontmatter
{
    "name": "dx-devops-test-suite-run",
    "metadata": {
        "version": "1.0",
        "minApiVersion": "67.0"
    },
    "description": "Runs DevOps Center test suites on a pipeline stage (Pre-Promote, Post-Promote, or Review event) end to end: triggers async execution via the Connect API after an explicit confirmation gate, then polls by runId at provider-specific intervals until it completes, fails, or times out, and hands results to failure analysis. Also retriggers a quality gate after fixes, but only once coverage meets the threshold. Use this skill when a user wants to run, kick off, or launch test suites on a stage, re-run a quality gate, or watch an in-progress run to completion. TRIGGER when: the user wants to run\/launch suites on a stage, execute tests before or after promotion, re-run a quality gate after fixing failures, unblock a blocked promotion after adding tests, or poll\/watch an in-progress run. DO NOT TRIGGER when: running sf apex run test directly (use platform-apex-test-run), or configuring a NEW gate or threshold (use dx-devops-test-pipeline-configure)."
}

Run a DevOps Center Test Suite

Triggers a DevOps Center test suite execution and watches it to completion. Running and polling are two halves of one operation — never poll without first having (or being handed) a runId.

API version: All DevOps testing system calls target Salesforce API v67.0 (minimum required).

Important: All DevOps Center data lives in the Salesforce org — NOT the local repo. Always query the org with sf data query or sf api request rest.


Prerequisites

Run the prerequisite checks in references/prerequisite-checks.md — Prerequisites 1–4 and Prerequisite 5 (stage), since this skill operates on a specific stage. You need the confirmed doce-org-alias, pipelineId, and stageId.

Inputs required

Input How to obtain
pipelineId Prerequisite 4 (pipeline selection)
stageId Prerequisite 5 (pipeline stage confirmation)
event Confirm with user: Pre-Promote, Post-Promote, or Review
testSuiteIds Confirmed suite IDs from selection or recommendation
doce-org-alias Prerequisite 1

Step 1 — Trigger execution

Confirmation gate

This call mutates org state — do not proceed without explicit user confirmation. Before calling the API, show:

"I'm about to run tests with the following configuration:

  • Pipeline: <pipelineName>
  • Stage: <stageName>
  • Event: <event>
  • Suite(s): <suiteName(s)>
  • Org: <doce-org-alias>

Shall I proceed?"

Do not make the API call until the user confirms.

API call

sf api request rest \
  "/services/data/v67.0/connect/devopstesting/pipeline/<pipelineId>/stage/execute" \
  --method POST \
  --body '{
    "stageId": "<stageId>",
    "event": "<event>",
    "testSuiteIds": ["<suiteId1>", "<suiteId2>"]
  }' \
  --target-org <doce-org-alias>
Field Type Description
stageId string The ID of the pipeline stage to execute tests on
event string Pre-Promote, Post-Promote, or Review
testSuiteIds string[] One or more test suite IDs to execute

On success

Extract the runId (execution ID) from the response. Inform the user:

"Tests are running in <doce-org-alias>. I'll update you when results are ready."

Then proceed immediately to Step 2 (polling) with the runId.

On error

See references/error-handling.md. If the org rejects execution (e.g. environmentId: null, or classIdList is null or empty — no tests to execute), read the actual error, explain the root cause and required fix in plain language, and finish cleanly. Do not retry in a loop and do not fabricate a runId or results.


Step 2 — Poll until completion

Confirmation required: No — polling is automatic and read-only.

Poll the execution record by runId at the provider-appropriate interval. Full intervals, timeout behavior, and the poll query are in references/polling-configuration.md.

Summary of the loop (the runId is a DevopsTestSuiteExecution Id — poll that object, not DevopsTestExecution):

  • Query DevopsTestSuiteExecution by runId each interval for Status, Coverage, SuccessCount, FailureCount, QualityGateStatus.
  • InProgress → wait and poll again.
  • Passed / Failed → surface Coverage, SuccessCount, FailureCount, and QualityGateStatus inline (no raw JSON). If FailureCount > 0, fetch the child DevopsTestExecution failure rows and hand off to dx-devops-test-failures-analyze.
  • Error → the run itself errored (not test failures); surface ResultDetails/Message in plain language and offer retry or skip.
  • Timeout → surface the runId, do NOT auto-retry, wait for user instruction.

Retrigger mode (re-running a quality gate)

Use when a promotion was blocked by a gate failure and the coverage gap has since been addressed. All preconditions, gate, and the retrigger API call are in references/retrigger-mode.md. Key rule: do not retrigger unless the latest Coverage meets or exceeds the DevopsQualityGateRule threshold. After the retrigger returns a new runId, hand it to Step 2 (polling).


Related skills

  • dx-devops-test-failures-analyze — receives the failure payload on completion; can also create a fix work item.
  • dx-devops-test-suite-assignments-configure — recommend which suites to run, or assign a suite to the stage if it isn't linked yet.
  • dx-devops-test-pipeline-configure — configure a new quality gate or threshold (this skill only re-runs existing gates).
用于执行Salesforce组织操作,包括创建Scratch Org、创建快照及在浏览器中打开Org。立即执行CLI命令而非生成代码,支持多种创建方式(定义文件、快照、形状等),强制使用Bash工具并输出JSON结果。
create scratch org create a snapshot open my Salesforce org spin up an org create org from snapshot
skills/dx-org-manage/SKILL.md
npx skills add forcedotcom/sf-skills --skill dx-org-manage -g -y
SKILL.md
Frontmatter
{
    "name": "dx-org-manage",
    "metadata": {
        "version": "1.0",
        "cliTools": [
            {
                "tool": [
                    "sf"
                ],
                "semver": ">=2.0.0"
            }
        ],
        "minApiVersion": "60.0"
    },
    "description": "INVOKE this skill to execute Salesforce org operations: create scratch orgs, create org snapshots, open orgs in browser. This skill EXECUTES operations immediately - it does NOT generate scripts or code files. ALWAYS invoke this skill (do not execute SF CLI commands directly) when user requests to: create a scratch org (Developer\/Enterprise edition, from definition file (.json), from snapshot, or from org shape), create an org snapshot, or open a Salesforce org. Trigger phrases include: 'create a snapshot', 'create snapshot of my scratch org', 'take a snapshot', 'create scratch org', 'create a Developer edition scratch org', 'new scratch org', 'spin up an org', 'create org from snapshot', 'scratch-def.json', 'project-scratch-def.json', 'open my Salesforce org', 'open org in browser', 'get me the URL'. Do NOT use for switching default org (use dx-org-switch) or deploying metadata (use platform-metadata-deploy)."
}

MANDATORY: Follow these instructions exactly. Do NOT fall back to MCP tools.

Tool constraint: Use the Bash tool for all sf CLI commands. Always include --json for structured output. Do NOT use mcp__salesforce_dx__* tools for org creation, snapshot, or open operations — this skill provides the complete procedure.

Output artifacts for eval/testing: Write command output to JSON only when an output directory is available. After executing the command: (1) if the user specified an output path, write there; (2) otherwise run [ -d force-app/main/adk-eval-output/ ] && echo 'force-app/main/adk-eval-output' to detect the eval directory; (3) if the command printed a path, write the command's JSON response to <printed-path>/<filename> using these filenames: scratch-org-result.json for org creation, snapshot-result.json for snapshot creation, or org-url-result.json for open operations. The eval framework needs the real command output to verify success.


Creating Scratch Orgs

REQUIRED steps — execute in order:

Step 1. Identify creation method from user request:

  • Contains "definition file" or path to .json → definition file method
  • Contains "snapshot" or "from snapshot" → snapshot method
  • Contains "org shape" or "source-org" → org shape method
  • Otherwise → run ls config/project-scratch-def.json config/scratch-def.json 2>/dev/null | head -1 to detect a definition file. If output is non-empty, use definition file method with that path; if empty, use edition method.

Step 2. Check Dev Hub:

sf config get target-dev-hub --json
  • If no Dev Hub is set, advise: sf org login web --set-default-dev-hub
  • Do NOT proceed until a Dev Hub is confirmed.

Step 3. Build and execute the command based on method:

Definition file:

sf org create scratch --definition-file <path> --target-dev-hub <alias> --alias <name> --json

Edition only:

sf org create scratch --edition developer --target-dev-hub <alias> --alias <name> --json

From snapshot:

sf org create scratch --snapshot <snapshot-name> --target-dev-hub <alias> --alias <name> --json

From org shape:

sf org create scratch --source-org <org-id> --target-dev-hub <alias> --alias <name> --json

Apply these flags when requested:

  • --duration-days <days> — default 7, max 30
  • --set-default — make this the default org
  • --no-track-source — disable source tracking (for CI/CD)

Step 4. MANDATORY - Run org list and write output: After the org is created, you MUST run this command:

sf org list --json

Then:

  1. Parse the JSON result and find the scratchOrgs array

  2. Find the entry where username matches the username from Step 3's creation result

  3. Extract that complete org object (it will include: alias, username, orgId, instanceUrl, loginUrl, isDefaultUsername, connectedStatus, lastUsed, etc.)

  4. Report to the user:

    • Created scratch org.
    • Alias: [alias from the org list entry]
    • Username: [username]
    • Org ID: [orgId]
  5. If an output directory is available (per the output artifacts rule above), write ONLY that extracted org object (NOT the full creation result) to <output-dir>/scratch-org-result.json

Example: If sf org list --json returns {"result": {"scratchOrgs": [{"alias": "feature-dev", "username": "test@example.com", "orgId": "00D...", ...}]}}, write just the inner org object {"alias": "feature-dev", "username": "test@example.com", "orgId": "00D...", ...} to the file.

Do NOT write the creation command's output. Do NOT suggest verification steps to the user.

Error handling:

  • "Snapshot not found" → suggest sf org list snapshot --target-dev-hub <alias>
  • "No default Dev Hub" → advise sf org login web --set-default-dev-hub

When you need more detail:

  • For available features, settings, and definition file structure → load references/definition_file_options.md
  • For edition selection guidance and comparison → load references/edition_types.md
  • For snapshot workflow and post-creation usage → load references/snapshot_usage.md
  • For complete scratch org creation workflow → load references/creating-scratch-org.md

Creating Snapshots

REQUIRED steps — execute in order:

Step 1. Get inputs:

  • Source org: scratch org ID or alias (from user)
  • Snapshot name: unique name (from user)
  • Description: optional (from user)

Step 2. Determine Dev Hub:

  • If user specifies a Dev Hub (alias or username) → use that value
  • Otherwise, check for default:
sf config get target-dev-hub --json
  • If no default Dev Hub is set, advise: sf org login web --set-default-dev-hub

Step 3. Execute:

sf org create snapshot --source-org <orgId-or-alias> --name <SnapshotName> --target-dev-hub <devHub> --json

With description:

sf org create snapshot --source-org <orgId-or-alias> --name <SnapshotName> --description "<desc>" --target-dev-hub <devHub> --json

Step 4. Report result: Returns JSON with SnapshotId and Status. If an output directory is available (per the output artifacts rule above), write the JSON response to <output-dir>/snapshot-result.json.

Error handling:

  • "NOT_FOUND" → Dev Hub doesn't have snapshot feature enabled
  • "Snapshot name already exists" → use a different unique name

When you need more detail:

  • For complete snapshot creation workflow and flag reference → load references/creating-snapshot.md
  • For CLI flag reference → load references/cli_flags.md

Opening Orgs

REQUIRED steps — execute in order:

Step 1. Match user request to command:

User wants Command
Open default org sf org open --json
Open specific org sf org open --target-org <alias> --json
Specific browser sf org open --browser chrome --json
Incognito mode sf org open --private --json
Navigate to path sf org open --path '<path>' --json
URL only (don't open) sf org open --url-only --json
Open metadata file sf org open --source-file <file-path> --json

Step 2. Execute the matching command using the Bash tool.

Step 3. Report result: Returns URL or opens browser. If an output directory is available (per the output artifacts rule above), write the JSON response to <output-dir>/org-url-result.json.

Error handling:

  • "no target org" → advise sf config set target-org <alias>
  • "auth error" → advise sf org login web --alias <alias>

When you need more detail:

  • For complete opening org workflow and all available flags → load references/opening-org.md

Reference File Index

Load these reference files for detailed guidance:

File When to read
references/definition_file_options.md User needs to configure org features, settings, or advanced definition file options beyond basic org creation
references/edition_types.md User asks which edition to choose or needs to understand edition differences
references/snapshot_usage.md User wants to use snapshots in definition files or needs post-snapshot workflow guidance
references/creating-scratch-org.md Troubleshooting scratch org creation failures or need complete workflow with all options
references/cli_flags.md User needs complete snapshot CLI flag reference
references/creating-snapshot.md Troubleshooting snapshot creation failures or need detailed snapshot workflow
references/opening-org.md User needs to navigate to specific setup paths, open metadata files, or use advanced open flags

Example Files

Example command outputs for testing and troubleshooting:

File Purpose
examples/scratch-orgs/success_definition_file.json Successful scratch org creation using --definition-file
examples/scratch-orgs/success_edition.json Successful scratch org creation using --edition developer
examples/scratch-orgs/success_snapshot.json Successful scratch org creation using --snapshot
examples/scratch-orgs/error_no_devhub.json Error when Dev Hub not authenticated
examples/scratch-orgs/error_timeout.json Timeout error during org creation (exit code 69)
examples/snapshots/success_output.json Successful snapshot creation
examples/snapshots/error_output.json Common snapshot error scenarios
用于在Salesforce CMS中搜索、提取并应用现有品牌指南(如语气、风格、颜色)到生成内容。适用于用户明确要求应用品牌标识或语境的场景,确保内容符合既定品牌规范。
apply my brand use our brand voice match our brand guidelines find my brand search for brand get brand instructions apply brand tone
skills/experience-cms-brand-apply/SKILL.md
npx skills add forcedotcom/sf-skills --skill experience-cms-brand-apply -g -y
SKILL.md
Frontmatter
{
    "name": "experience-cms-brand-apply",
    "metadata": {
        "version": "1.0"
    },
    "description": "Extracts, retrieves, and applies CMS brand guidelines (voice, tone, style, colors, typography) to generated content. Use this skill ANY TIME a user request involves branding, brand voice, brand tone, brand guidelines, brand identity, brand styling, or applying a brand to content. Triggers for requests like \"apply my brand\", \"use our brand voice\", \"match our brand guidelines\", \"find my brand\", \"search for brand\", \"get brand instructions\", \"apply brand tone\". Handles the full workflow: searching for brands in Salesforce CMS, extracting brand instructions, and applying brand voice\/tone\/guidelines to generated content. Does not apply to media\/image search (use experience-content-media-search skill), logo search, or creating new brand definitions.",
    "compatibility": "Requires get_brand_instructions and\/or search_brands MCP tools"
}

Applying CMS Brand

Universal skill for searching, extracting, and applying CMS brand guidelines to generated content.

Scope

This skill is for APPLYING existing brand guidelines from Salesforce CMS to content you generate.

Use this skill when the user wants to:

  • Apply their brand voice/tone to generated content
  • Find and use brand guidelines stored in Salesforce CMS
  • Search for an existing brand in their org
  • Get brand instructions for content generation
  • Ensure generated content matches their brand identity
  • Apply brand styling, tone, or voice to a page, component, or app

DO NOT use this skill when the user wants to:

  • Search for images or media (use experience-content-media-search skill)
  • Create a new brand from scratch
  • Edit brand definitions in CMS
  • Generate logos or visual brand assets

Before You Start

CRITICAL: You must retrieve brand instructions BEFORE applying any brand.

When a user requests branded content:

  1. Search for available brands (if brand is not already identified)
  2. Extract brand instructions for the selected brand
  3. Apply brand guidelines to all content you generate

Never generate content first and retrofit branding later. Brand instructions must inform content generation from the start.

Workflow Overview

Copy this checklist and track your progress:

CMS Branding Progress:
- [ ] Step 1: Determine if brand is already identified or needs search
- [ ] Step 2: Search for brands (if needed) and present options to user
- [ ] Step 3: Extract brand instructions for the selected brand

Step 1: Determine Brand Context

Check if the user has already specified which brand to use:

Brand is known (user named it, or only one brand exists):

  • Skip to Step 3 (Extract Brand Instructions)

Brand is unknown (user says "apply my brand" without specifying which):

  • Proceed to Step 2 (Search for Brands)

Step 2: Search for Brands

Tool: search_brands

Process:

  1. Determine search query — Use the user's description, company name, or a general keyword
  2. Build the request:
{
  "inputs": [{
    "searchQuery": "keyword or brand name"
  }]
}
  1. Call search_brands with the query
  2. Parse the response — Extract brand results:
    • managedContentId — Unique ID (use this for extraction in Step 3)
    • managedContentKey — Content key identifier
    • title — Brand display name
    • contentUrl — URL to the brand content
    • totalResults — Number of brands found

Presenting Brand Results

If multiple brands found, use ask_followup_question to present options:

I found [N] brands in your CMS. Which one should I apply?

1. [Brand Title 1]
2. [Brand Title 2]
3. [Brand Title 3]

Which brand would you like to use?

If one brand found, confirm with the user:

I found the brand "[Brand Title]". Should I apply this brand's guidelines to the content?

If no brands found:

No brands found in Salesforce CMS. To use branding:
1. Create a brand in Salesforce CMS (Content Type: sfdc_cms__brand)
2. Provide brand guidelines directly in this conversation

Would you like to proceed without CMS branding, or provide guidelines manually?

Never auto-select a brand without confirmation. Always wait for user choice.

Step 3: Extract Brand Instructions

Tool: get_brand_instructions

Process:

  1. Call get_brand_instructions — This retrieves the branding extraction prompt template

  2. Parse the response:

    • promptBody — Contains the full brand instruction prompt with extraction and application rules
  3. Follow the instructions in promptBody — The prompt template contains specific guidance on:

    • How to extract brand properties from the brand content
    • Brand voice and tone rules
    • Typography and color guidelines
    • Content formatting rules
    • Guardrails and restrictions

What Brand Instructions Contain

The extracted brand instructions typically include:

Property Description
Brand Voice How the brand speaks (e.g., professional, friendly, authoritative)
Brand Tone Emotional quality of communication (e.g., confident, warm, empathetic)
Key Messages Core messaging pillars and value propositions
Content Rules Dos and don'ts for content generation
Style Guidelines Typography, color, spacing preferences
Guardrails Hard restrictions on language, topics, or claims

Error Handling

Error Response
search_brands unavailable "Brand search is unavailable. Please provide your brand name or guidelines directly."
get_brand_instructions unavailable "Cannot retrieve brand instructions. Please share your brand guidelines in this conversation and I'll apply them manually."
Org lacks Vibes branding "CMS branding is not enabled for this org. Contact your admin to enable the Agentforce Vibes branding feature."
Permission denied "You don't have permission to access CMS brands. Ensure you have Managed Content Authoring permission."
Brand extraction returns empty "The brand exists but has no configured guidelines. Please add brand properties in CMS or provide guidelines here."

Never silently fail. Always inform the user and offer alternatives.

Key Principles

  1. Brand first, content second — Always extract brand instructions before generating content
  2. Never assume brand guidelines — Only apply what was explicitly retrieved from CMS
  3. Respect guardrails absolutely — Brand content rules are hard constraints, not suggestions
  4. Confirm brand selection — Never auto-select a brand without user confirmation
  5. Show your work — Tell the user which guidelines you applied and how
  6. Graceful degradation — If tools are unavailable, ask for manual guidelines rather than proceeding without branding
用于在UI Bundle项目中集成Agentforce对话客户端。当项目包含uiBundles目录且需添加或修改聊天组件时触发。严禁从零创建自定义组件,必须使用现有<AgentforceConversationClient />并验证agentId格式。
用户要求在UI Bundle项目中嵌入、配置或移除聊天机器人或AI助手 项目中存在uiBundles/*/src/目录且任务涉及聊天功能开发 文件导入AgentforceConversationClient
skills/experience-ui-bundle-agentforce-client-generate/SKILL.md
npx skills add forcedotcom/sf-skills --skill experience-ui-bundle-agentforce-client-generate -g -y
SKILL.md
Frontmatter
{
    "name": "experience-ui-bundle-agentforce-client-generate",
    "metadata": {
        "package": "@salesforce\/ui-bundle-template-feature-react-agentforce-conversation-client",
        "version": "1.1",
        "sdk-package": "@salesforce\/agentforce-conversation-client"
    },
    "description": "Use this skill when the user asks to add, embed, integrate, configure, style, or remove an agent, chatbot, chat widget, conversation client, or AI assistant in a UI Bundle project. TRIGGER when: project contains a uiBundles\/*\/src\/ directory and the task involves adding or modifying a chat widget, chatbot, or conversational AI; files under uiBundles\/*\/src\/ import AgentforceConversationClient; user asks to add any chat or agent functionality to a page. DO NOT TRIGGER when: user wants to create a custom agent, chatbot, or chat widget component from scratch; the project has no uiBundles directory."
}

Managing Agentforce Conversation Client

HARD CONSTRAINT: NEVER create a custom agent, chatbot, or chat widget component. ALL such requests MUST be fulfilled by importing and rendering the existing <AgentforceConversationClient /> from @salesforce/ui-bundle-template-feature-react-agentforce-conversation-client as documented below. If a requirement is unsupported by this component's props, state the limitation — do not improvise an alternative.

Prerequisites

Before the component will work, the following Salesforce settings must be configured by the user. ALWAYS call out the prequisites after successfully embedding the agent.

Trusted domains (required only for local development):

  • Setup → Session Settings → Trusted Domains for Inline Frames → Add your domain
    • Local development: localhost:5173 (default Vite dev server port)
    • Warning: Remove this trusted domain entry before deploying to production.

Instructions

Step 1: Check if component already exists

Search for existing usage across all app files (not implementation files):

grep -r "AgentforceConversationClient" --include="*.tsx" --include="*.jsx" --exclude-dir=node_modules

Important: Look for React files that import and USE the component (for example, shared shells, route components, or feature pages). Do NOT open files named AgentforceConversationClient.tsx or AgentforceConversationClient.jsx - those are the component implementation.

If multiple files found: Ask the user which component file they are referring to. Do not proceed until clarified.

If found: Read the file and check the current agentId value.

Agent ID validation rule (deterministic):

  • Valid only if it matches: ^0Xx[a-zA-Z0-9]{15}$
  • Meaning: starts with 0Xx and total length is 18 characters

Decision:

  • If agentId matches ^0Xx[a-zA-Z0-9]{15}$ and user wants to update other props → Go to Step 4 (update props)
  • If agentId matches ^0Xx[a-zA-Z0-9]{15}$ and user asks to "embed" or "add" the chat client → Inform: "The Agentforce Conversation Client is already embedded in <file> with agent ID <agentId>. Would you like to change the agent or update other props?"
    • Change agent → Step 2
    • Update props → Step 4b
  • If agentId is missing, empty, or does NOT match ^0Xx[a-zA-Z0-9]{15}$ → Continue to Step 2 (need real ID)
  • If not found → Continue to Step 2 (add new)

If user reports an error:

If the user says the component is "not working", "showing an error", or similar — ask them for the specific error message. Then proceed to Step 2 to cross-check the configured agentId against the org.

Step 2: Resolve and Validate Agent ID

Prerequisites

  1. Verify sf CLI is available:

    sf --version
    

    If fails:

    • Inform: "The Salesforce CLI (sf) is not installed. It's needed to query available agents from your org."
    • Ask: "Would you like me to install it?"
      • Yes → Install via npm install -g @salesforce/cli, then continue.
      • No → "You can find your agent ID manually in Setup → Agentforce Agents → click the agent name → copy the ID from the URL. Would you like to provide it now, or skip this step?"
        • User provides ID → validate format (^0Xx[a-zA-Z0-9]{15}$), store it, proceed to Step 3.
        • Skip → proceed to Step 4 with placeholder <YOUR_AGENT_ID>.
  2. Verify org connectivity:

    sf org display --json
    

    If fails:

    • Inform: "No authenticated org found."
    • Ask: "Would you like to connect to your org now? Run sf org login web to authenticate."
      • User authenticates → retry the query, continue.
      • User declines → "You can find your agent ID manually in Setup → Agentforce Agents → click the agent name → copy the ID from the URL. Would you like to provide it now, or skip this step?"
        • User provides ID → validate format, store it, proceed to Step 3.
        • Skip → proceed to Step 4 with placeholder <YOUR_AGENT_ID>.

Note: Even if the user provides their own agentId, the org must be connected for the agent to function at runtime. An agentId without a connected org will not work.

Query all Employee Agents

Run the SOQL query defined in references/agent-id-resolution.md.

Handle results

No records at all:

"No Employee Agents found in this org. Create one in Setup → Agentforce Agents."

Ask user if they want to provide an agent ID manually or skip. If skip, proceed to Step 4 with placeholder <YOUR_AGENT_ID>.

All agents are inactive:

Found Employee Agents but none are active:

  • Agentforce Sales Agent (0Xxxx000000001dCAA)
  • HR Assistant (0Xxxx0000000002BBB)

To activate: Setup → Agentforce Agents → click the agent name → open in Agent Builder → press Activate. Then re-run this step.

Ask user if they want to provide an agent ID manually or skip. If skip, proceed to Step 4 with placeholder <YOUR_AGENT_ID>.

Has active agents — Path A (fresh install / no existing agentId):

Present only active agents for selection:

Which agent should the chat widget use?

  1. Property Manager Agent (0Xxxx0000000001CAA)
  2. HR Assistant (0Xxxx0000000002BBB)
  • One agent → still confirm with user, do not auto-select.
  • If user picks one → store the selected Id for use in Step 4.
  • If user declines to pick ("skip", "no", "I don't want to set one") → accept it and move to next steps. Do not re-ask. In Step 4, use placeholder <YOUR_AGENT_ID> for fresh installs. For existing projects, leave the component as-is.

Has active agents — Path B (existing agentId from Step 1, passed format check):

Cross-check the existing agentId against query results:

  • ID found, agent is Active → "Agent ID maps to 'Property Manager Agent' — active in the org." Proceed.
  • ID found, agent is Inactive → "The configured agent 'Sales Agent' exists but is Inactive. To activate: Setup → Agentforce Agents → click the agent name → open in Agent Builder → press Activate. Or pick a different active agent:" → show active list.
  • ID not found at all → "The configured agent (0Xxxx...) doesn't exist in this org — it may have been deleted or belongs to a different org. Pick a replacement:" → show active list. If no active agents available, show inactive list with activation instructions.

If user reported an error → surface the agent name even if active, so user can confirm it's the intended one.

Query error handling

If the SOQL query fails, surface the error message from the response directly to the user. Do not guess at the fix — just report what came back. For example:

"The query failed with: [error message from response]. Check your org permissions or that the API version supports this object."

What this step does NOT do

  • No fallback to GraphQL or Tooling API — SOQL only
  • No auto-selection (always confirm with user)
  • No programmatic activation (only via Setup UI)
  • No file writes (that's Step 4)

Step 3: Canonical import strategy

Use this import path by default in app code:

import { AgentforceConversationClient } from "@salesforce/ui-bundle-template-feature-react-agentforce-conversation-client";

If the package is not installed, install it:

npm install @salesforce/ui-bundle-template-feature-react-agentforce-conversation-client

Only use a local relative import (for example, ./components/AgentforceConversationClient) when the user explicitly asks to use a patched/local component in that app.

Do not infer import path from file discovery alone. Prefer one consistent package import across the codebase.

Step 4: Add or update component

Determine which sub-step applies:

  • Component NOT found in Step 1 → go to 4a (New installation)
  • Component found in Step 1 → go to 4b (Update existing)

4a — New installation

  1. If the user already specified a target file, use that file. Otherwise, ask the user: "Which file should I add the AgentforceConversationClient to?" Do NOT proceed until a target file is confirmed.
  2. Read the target file to understand its existing imports and TSX structure.
  3. Add the import at the top of the file, alongside existing imports. Use the canonical package import from Step 3:
import { AgentforceConversationClient } from "@salesforce/ui-bundle-template-feature-react-agentforce-conversation-client";
  1. Insert the <AgentforceConversationClient /> TSX into the component's return block. Place it as a sibling of existing content — do NOT wrap or restructure existing TSX. Use the real agentId obtained in Step 2. If no agentId was resolved (user skipped Step 2), use the placeholder:

With resolved agentId:

<AgentforceConversationClient agentId="0Xx8X00000001AbCDE" />

Without resolved agentId (user skipped):

<AgentforceConversationClient agentId="<YOUR_AGENT_ID>" />
  1. Do NOT add any other code (wrappers, layout components, new functions) unless the user explicitly requests it.

4b — Update existing

  1. Read the file identified in Step 1.
  2. Locate the existing <AgentforceConversationClient ... /> TSX element.
  3. Apply only the changes the user requested. Rules:
    • Add new props that the user asked for.
    • Change prop values the user asked to update.
    • Preserve every prop and value the user did NOT mention — do not remove, reorder, or reformat them.
    • Never delete the component and recreate it.
  4. If Step 2 was triggered (cross-check or fresh selection) and a new agent ID was resolved, replace the existing agentId value with the new one.
  5. If the current agentId is already valid and the user did not ask to change it and Step 2 confirmed it is active, leave it as-is.

Post-Step-4 error handling

If the user reports an error after the component has been set up (e.g., "it's not working", "I see an error"), go to Step 2 to validate the configured agentId against the org. Cross-check whether the agent is active, exists, and belongs to the connected org.

Step 5: Configure props

Available props (use directly on component):

  • agentId (string, required) - Salesforce agent ID
  • inline (boolean) - true for inline mode, omit for floating
  • width (number | string) - e.g., 420 or "100%"
  • height (number | string) - e.g., 600 or "80vh"
  • headerEnabled (boolean) - Show/hide header
  • styleTokens (object) - For all styling (colors, fonts, spacing)
  • salesforceOrigin (string) - Auto-resolved
  • frontdoorUrl (string) - Auto-resolved
  • agentLabel (string) - header title for agent

Examples:

Floating mode (default):

<AgentforceConversationClient agentId="0Xx..." />

Inline mode with dimensions:

<AgentforceConversationClient agentId="0Xx..." inline width="420px" height="600px" />

Adding or updating agent label:

<AgentforceConversationClient agentId="0Xx..." agentLabel="<dummy-agent-label>" />

Styling rules (mandatory):

  • ALL visual customization (colors, fonts, spacing, borders, radii, shadows) MUST go through the styleTokens prop. There are no exceptions.
  • ONLY use token names listed in the tables below. Do NOT invent custom token names.
  • NEVER apply styling via CSS files, style attributes, className, or wrapper elements. These approaches will not work and will be ignored by the component.
  • If the user requests a visual change that does not map to a token below, inform them that the change is not supported by the current token set.

For the complete list of available style tokens, consult references/style-tokens.md.

For complex patterns, consult references/examples.md for:

  • Sidebar containers and responsive sizing
  • Dark theme and advanced theming combinations
  • Inline without header, calculated dimensions
  • Complete host component examples

Common mistakes to avoid: Consult references/constraints.md for:

  • Invalid props (containerStyle, style, className)
  • Invalid styling approaches (CSS files, style tags)
  • What files NOT to edit (implementation files)

Common Issues

If component doesn't appear or authentication fails, see references/troubleshooting.md for:

  • Agent activation and deployment
  • Localhost trusted domains
  • Cookie restriction settings

Reference File Index

File When to read
references/agent-id-resolution.md Step 2 — SOQL query structure, response format, activation path, manual lookup
references/style-tokens.md Step 5 — Complete style token reference for all UI areas
references/examples.md Step 5 — Layout patterns, sizing, theming combinations, host component examples
references/constraints.md Step 4 — Invalid props, invalid styling approaches, files not to edit
references/troubleshooting.md Post-setup — Agent activation, trusted domains, cookie settings
用于从零开始构建完整的Salesforce React UI Bundle应用。通过协调脚手架、功能安装、数据访问和UI生成等专用技能,按依赖顺序实现端到端的全栈前端应用开发。
用户要求构建React应用、Web应用或单页应用 项目包含uiBundles目录且需从头创建新应用 任务涉及多个UI Bundle技能的协同工作
skills/experience-ui-bundle-app-coordinate/SKILL.md
npx skills add forcedotcom/sf-skills --skill experience-ui-bundle-app-coordinate -g -y
SKILL.md
Frontmatter
{
    "name": "experience-ui-bundle-app-coordinate",
    "metadata": {
        "version": "1.0",
        "relatedSkills": "experience-ui-bundle-metadata-generate, experience-ui-bundle-features-generate, experience-ui-bundle-salesforce-data-access, experience-ui-bundle-frontend-generate, experience-ui-bundle-agentforce-client-generate, experience-ui-bundle-file-upload-generate, experience-ui-bundle-deploy, experience-ui-bundle-site-generate, experience-ui-bundle-custom-app-generate"
    },
    "description": "MUST activate when the user wants to build, create, or generate a React application, React app, web application, single-page application (SPA), or frontend application — even if no project files exist yet. MUST also activate when the project contains a uiBundles\/*\/src\/ directory or sfdx-project.json and the prompt says create, build, construct, or generate a new app, site, or page from scratch — even if the prompt also describes visual styling. MUST also activate when the task spans more than one ui-bundle skill. Use this skill when building a complete app end-to-end. Do NOT use for Lightning Experience apps with custom objects (use platform-lightning-app-coordinate). Do NOT use for single-concern edits to an existing page (use experience-ui-bundle-frontend-generate)."
}

Building a UI Bundle App

Overview

Build a complete, deployable Salesforce React UI bundle application from a natural language description by orchestrating specialized UI bundle skills in correct dependency order. Each skill MUST be explicitly loaded before executing its phase.

When to Use This Skill

Use when:

  • User requests a "React app", "UI bundle", "web app", or "full-stack app" on Salesforce
  • User says "build an app", "create an application" and the context implies a non-LWC based frontend (e.g. React)
  • The work produces a complete UI bundle with scaffolding, features, data access, and UI -- not a single component in isolation

Examples that should trigger this skill:

  • "Build a React app for managing customer cases with Salesforce data"
  • "Create a UI bundle for an employee directory with search and navigation"
  • "I need a full-stack React app with authentication, data tables, and file uploads"
  • "Build a coffee shop ordering app on Salesforce"

Do NOT use when:

  • Creating a single page or component (use experience-ui-bundle-frontend-generate)
  • Only installing a feature (use experience-ui-bundle-features-generate)
  • Only setting up data access (use experience-ui-bundle-salesforce-data-access)
  • Only deploying an existing app (use experience-ui-bundle-deploy)
  • Building a Lightning Experience app with custom objects and metadata (use platform-lightning-app-coordinate)
  • Troubleshooting or debugging an existing UI bundle

Dependency Graph & Build Order

Phase 1: Scaffolding (Foundation)

UI Bundle scaffold (sf template generate ui-bundle)
    v
Install dependencies (npm install)
    v
Bundle metadata (uibundle-meta.xml, ui-bundle.json)
    v
CSP Trusted Sites (if external domains needed)

Creates the UI bundle directory structure, meta XML, and optional routing/headers config. All subsequent phases require the scaffold to exist.

Phase 2: Features (Optional)

Search project code (src/) for existing implementations
    v
Install dependencies (npm install)
    v
Search, describe, and install features (auth, shadcn, search, navigation, GraphQL)
    v
Resolve conflicts (two-pass: --on-conflict error, then --conflict-resolution)
    v
Integrate __example__ files into target files, then delete them

Installs pre-built, tested feature packages. Skip if the app requires no pre-built features. Always check for an existing feature before building from scratch. Features provide the foundation that UI components build on top of.

Phase 3: Data Access (Backend Wiring)

Ground every entity/field against the org (per experience-ui-bundle-salesforce-data-access)
    v
Generate queries/mutations FROM the verified names (never from guessed fields)
    v
Generate types (npm run graphql:codegen) and wire into components
    v
Validate and test (npx eslint, ask user before testing mutations)

Sets up the data layer using the @salesforce/platform-sdk Data SDK (createDataSDK().graphql). GraphQL is preferred for record operations; REST for Connect, Apex, or UI API endpoints. The experience-ui-bundle-salesforce-data-access skill owns the grounding + authoring workflow — load it and follow it; do not substitute a local-schema grep or guessed field names. Grounding happens against the live org, so it does not require a local schema.graphql to be present.

Phase 4: UI (Frontend)

Layout, navigation, header, and footer (appLayout.tsx)
    v
Pages (routed views)
    v
Components (widgets, forms, tables)

Builds the React UI. References the data layer from Phase 3 and the features from Phase 2. Must replace all boilerplate and placeholder content.

Phase 5: Integrations (Optional)

Agentforce chat widget (if requested)
File upload API (if requested)

These are independent and can be executed in parallel if both are needed.

Phase 6: Deployment

Org authentication
    v
Pre-deploy UI bundle build (npm install + npm run build)
    v
Deploy metadata
    v
Post-deploy configuration (permissions, profiles, named credentials, connected apps, custom settings, flow activation)
    v
Import data (if data plan exists)
    v
Fetch GraphQL schema and run codegen
*(Re-fetches schema from the deployed org -- required because the remote schema may differ from the local one used in Phase 3)*
    v
Final UI bundle build (rebuilds with the deployed schema)

Follows the canonical 7-step deployment sequence. Must deploy metadata before fetching schema. Must assign permissions before schema fetch.

Phase 7: Hosting Target

Choose one of the following based on the app's audience:

Phase 7a: Experience Site (External)

Resolve site properties (siteName, appDevName, etc.)
    v
Generate site metadata (Network, CustomSite, DigitalExperience)
    v
Deploy site infrastructure

Creates the Digital Experience site that hosts the UI bundle. Use when the user wants a public-facing or authenticated site URL for external users.

Phase 7b: Custom Application (Internal)

Resolve app properties (appName, appNamespace, appLabel)
    v
Generate CustomApplication metadata (applications/*.app-meta.xml)
    v
Add <target>CustomApplication</target> to .uibundle-meta.xml
    v
Deploy custom application

Creates a Custom Application entry in the Lightning App Launcher. Use when the app is for internal users accessing it within Lightning Experience.


Execution Workflow

STEP 1: Requirements Analysis & Planning

Actions:

  1. Parse the user's natural language request
  2. Identify the app name and purpose
  3. Extract pages and navigation structure
  4. Identify data entities and Salesforce objects needed
  5. Detect feature requirements (authentication, search, file upload, chat)
  6. Determine if an Experience Site is needed
  7. Identify external domains for CSP registration

The plan MUST contain an explicit grounding step before any query authoring. Do not list guessed object/field names as settled facts and defer verification to codegen. The data-access portion of the plan must read: "verify these entities/fields against the org (via experience-ui-bundle-salesforce-data-access), then author queries from the verified names." A plan that authors queries first and codegens later is the failure mode that produces guessed fields and hand-stubbed types — do not emit it.

Output: Build Plan

UI Bundle App Build Plan: [App Name]

SCAFFOLDING:
- App name: [PascalCase name]
- Routing: [SPA rewrites, trailing slash config]
- External domains: [domains needing CSP registration]

FEATURES:
- [list of features to install: auth, shadcn, search, navigation, etc.]

DATA ACCESS:
- Objects: [Salesforce objects to query/mutate]
- Grounding: [verify each object + its fields against the org via experience-ui-bundle-salesforce-data-access BEFORE authoring — list the objects/fields to confirm, not assumed-correct names]
- Queries: [GraphQL queries to author FROM the verified names]
- REST endpoints: [only where GraphQL/uiapi genuinely cannot cover it — not as a fallback for fields that were hard to verify]

UI:
- Layout: [description of app shell/navigation]
- Pages: [list of pages with routes]
- Components: [key components per page]
- Design direction: [aesthetic/style intent]

INTEGRATIONS (if applicable):
- Agentforce chat: [yes/no, agent ID if known]
- File upload: [yes/no, record linking pattern]

DEPLOYMENT:
- Target org: [org alias if known]
- Hosting target: [Experience Site / Custom Application / none]

SKILL LOAD ORDER:
1. experience-ui-bundle-metadata-generate
2. experience-ui-bundle-features-generate (if features needed)
3. experience-ui-bundle-salesforce-data-access (if data access needed)
4. experience-ui-bundle-frontend-generate
5a. experience-ui-bundle-agentforce-client-generate (if chat requested)
5b. experience-ui-bundle-file-upload-generate (if file upload requested)
6. experience-ui-bundle-deploy
7a. experience-ui-bundle-site-generate (if Experience Site requested -- external users)
7b. experience-ui-bundle-custom-app-generate (if Custom Application requested -- internal users)

STEP 2: Per-Phase Execution

Execute each phase sequentially. Complete all steps within a phase before moving to the next. For each phase:

Step What to do Why
1. Load skill Invoke the skill (e.g., via the Skill tool) for this phase Gives you the current rules, patterns, constraints, and implementation guides
2. Execute Follow the loaded skill's workflow to generate code/config The skill defines HOW to do the work correctly
3. Verify Run lint and build from the UI bundle directory Catch errors before moving to the next phase
4. Checkpoint Confirm phase completion before proceeding Ensures dependencies are satisfied for the next phase

Do NOT skip step 1 (loading the skill). Even if you remember the skill's content, skills evolve. Always load the current version.


Phase 1 -- Scaffolding

    1. Load skill: Invoke experience-ui-bundle-metadata-generate
    1. Execute: Run sf template generate ui-bundle, install dependencies (npm install), configure meta XML, ui-bundle.json, and CSP trusted sites
    1. Verify: Confirm directory structure and metadata files exist
    1. Checkpoint: UI bundle scaffold is ready -- proceed to Phase 2

Phase 2 -- Features (skip if no pre-built features needed)

    1. Load skill: Invoke experience-ui-bundle-features-generate
    1. Execute: Install dependencies, search and install features, integrate example files
    1. Verify: Run npm run build to confirm features integrate cleanly
    1. Checkpoint: Features installed -- proceed to Phase 3

Phase 3 -- Data Access (skip if no Salesforce data needed)

    1. Load skill: Invoke experience-ui-bundle-salesforce-data-access
    1. Execute: Ground entities/fields against the org first (per the skill), then author queries/mutations from the verified names, run codegen, wire into components. Never guess fields or hand-edit generated types.
    1. Verify: Run npx eslint on files with GraphQL queries
    1. Checkpoint: Data layer ready -- proceed to Phase 4

Phase 4 -- UI

    1. Load skill: Invoke experience-ui-bundle-frontend-generate
    1. Execute: Build layout, pages, components, navigation. Replace all boilerplate.
    1. Verify: Run lint and build -- 0 errors required
    1. Checkpoint: UI complete -- proceed to Phase 5

Phase 5 -- Integrations (skip if not requested)

    1. Load skill(s): Invoke experience-ui-bundle-agentforce-client-generate (5a) and/or experience-ui-bundle-file-upload-generate (5b). If both are needed, they are independent and can be executed in parallel.
    1. Execute: Follow each skill's workflow to add the integration
    1. Verify: Run lint and build
    1. Checkpoint: Integrations complete -- proceed to Phase 6

Phase 6 -- Deployment

    1. Load skill: Invoke experience-ui-bundle-deploy
    1. Execute: Follow the 7-step deployment sequence (auth, build, deploy, permissions, data, schema, final build)
    1. Verify: Confirm deployment succeeds and app is accessible
    1. Checkpoint: App deployed -- proceed to Phase 7 if needed

Phase 7a -- Experience Site (skip if not requested or if Custom Application chosen)

    1. Load skill: Invoke experience-ui-bundle-site-generate
    1. Execute: Resolve properties, generate site metadata, deploy
    1. Verify: Confirm site URL is accessible
    1. Checkpoint: Site live -- build complete

Phase 7b -- Custom Application (skip if not requested or if Experience Site chosen)

    1. Load skill: Invoke experience-ui-bundle-custom-app-generate
    1. Execute: Resolve app properties, generate CustomApplication metadata, add CustomApplication target to meta XML
    1. Verify: Confirm app appears in App Launcher
    1. Checkpoint: App registered -- build complete

STEP 3: Final Summary

After all phases complete, present a build summary:

UI Bundle App Build Complete: [App Name]

PHASES COMPLETED:
[x] Phase 1: Scaffolding -- [app name] UI bundle created
[x] Phase 2: Features -- [list of features installed, or "skipped"]
[x] Phase 3: Data Access -- [list of entities wired up]
[x] Phase 4: UI -- [count] pages, [count] components
[x] Phase 5: Integrations -- [list or "none"]
[x] Phase 6: Deployment -- deployed to [org]
[x] Phase 7: Hosting Target -- [Experience Site URL / Custom Application name / "skipped"]

FILES GENERATED:
[list key files and their paths]

NEXT STEPS:
[any manual steps the user should take]

Validation

Before presenting the build as complete, verify:

  • Scaffold exists: UI bundle directory with valid meta XML and ui-bundle.json
  • Dependencies installed: node_modules/ exists and package.json has expected packages
  • Build passes: npm run build produces dist/ with no errors
  • Lint passes: npx eslint src/ reports 0 errors
  • No boilerplate: All placeholder text, default titles, and template content has been replaced
  • Navigation works: appLayout.tsx has real nav items matching created pages
  • Data layer wired: Components use the @salesforce/platform-sdk Data SDK (createDataSDK().graphql), with all entities/fields grounded against the org — not guessed (if data access phase was executed)
  • CSP registered: All external domains have CSP Trusted Site metadata (if applicable)

Error Handling

Category 1: Stop and Ask User

  • App purpose is too vague to determine pages or data needs
  • User wants features that conflict (e.g., "no authentication" + "show user-specific data")
  • Target org is unknown and deployment is requested

Category 2: Log Warning, Continue

  • A feature install has minor conflicts (resolve and continue)
  • Optional integration setup encounters non-blocking issues
  • Build has non-error warnings

Best Practices

1. Always Follow Phase Order

Never build UI before installing features. Never deploy before building. Dependencies are strict.

2. Replace All Boilerplate

Every generated app must feel purpose-built. Replace "React App" titles, "Vite + React" placeholders, and all default content with real app-specific text and branding.

3. Design with Intent

Follow the design thinking and frontend aesthetics guidance from experience-ui-bundle-frontend-generate. Every app should have a clear visual direction -- not generic defaults.

用于在Salesforce Lightning Experience中生成和配置Custom Application,以托管React UI Bundle并使其显示在App Launcher中。适用于存在uiBundles目录或需修改.app-meta.xml文件的场景,区别于Experience Sites。
项目包含 uiBundles/*/src/ 目录且任务涉及创建或配置 Custom Application 存在 applications/*.app-meta.xml 文件且需要修改 用户希望将应用通过 Lightning App Launcher 暴露给内部用户
skills/experience-ui-bundle-custom-app-generate/SKILL.md
npx skills add forcedotcom/sf-skills --skill experience-ui-bundle-custom-app-generate -g -y
SKILL.md
Frontmatter
{
    "name": "experience-ui-bundle-custom-app-generate",
    "metadata": {
        "version": "1.0"
    },
    "description": "MUST activate when the project contains a uiBundles\/*\/src\/ directory and the task involves creating or configuring a Custom Application for hosting a UI bundle in Lightning Experience. Use this skill when creating a CustomApplication metadata record to surface the UI bundle in the App Launcher. Activate when files matching applications\/*.app-meta.xml exist and need modification, or when the user wants to expose their app via the Lightning App Launcher without a Digital Experience Site. Do NOT use platform-custom-application-generate for this — UI bundle apps do not use tabs, action overrides, or flexipages."
}

Custom Application for React UI Bundles

Create and configure a Salesforce Custom Application that hosts a React UI bundle in Lightning Experience. This skill generates the CustomApplication metadata so the app appears in the Lightning App Launcher and can be accessed by internal users.

Custom Applications differ from Experience Sites: they don't need Networks, CustomSite, DigitalExperienceConfig, or DigitalExperienceBundle metadata. The Custom Application acts as a thin launcher entry that delegates rendering to the React UI bundle referenced by uiBundle.

Required Properties

Resolve all properties before generating any metadata. Each has a fallback chain — work through each option in order until a value is found.

Property Format How to Resolve
appName lowercamelcase (e.g., myInternalApp) The UI bundle name from uiBundles/<name>/ directory
appNamespace String namespace in sfdx-project.jsonsf data query -q "SELECT NamespacePrefix FROM Organization" --target-org ${usernameOrAlias} → default c
appLabel Human-readable string User-provided, or derive from appName by converting camelCase to Title Case

The appNamespace and appName connect the Custom Application to the correct React UI bundle. In newer API versions this uses <uiBundle>{appNamespace}__{appName}</uiBundle>; in older versions it uses <webApplication>{appName}</webApplication>. Getting this wrong means the app launcher entry exists but shows a blank page. Step 2 of the workflow determines which field to use.

Generation Workflow

Step 1: Resolve All Required Properties

Determine values for all properties before constructing anything. Use the resolution strategies in the table above.

Step 2: Query API Context (Version-Aware Field Discovery)

Call salesforce-api-context MCP tools to discover which fields exist for the target org's API version. This ensures the generated metadata is compatible with the user's Salesforce version.

Required calls:

  1. Call get_metadata_type_fields for CustomApplication — check whether the uiBundle field exists
  2. Call get_metadata_type_fields for UIBundle — check whether the target field exists

Field resolution based on API response:

Field Check If present If absent (older API version)
CustomApplication.uiBundle Use <uiBundle>{appNamespace}__{appName}</uiBundle> Use <webApplication>{appName}</webApplication> (no namespace)
UIBundle.target Use <target>CustomApplication</target> Omit the <target> element entirely

If salesforce-api-context is unavailable after a real attempt, fall back to the newer field names (uiBundle + target).

Step 3: Create the Project Structure

Create any files and directories that don't already exist:

Metadata Type Path
CustomApplication <sourceDir>/applications/{appName}.app-meta.xml

Note: <sourceDir> is determined from sfdx-project.json. Read packageDirectories[] and use the entry where "default": true; the full source directory is <path>/main/default. If no default is set, use the first entry. Commonly force-app/main/default, but this path is configurable.

Step 4: Populate All Metadata Fields

Use the default template in the doc below. Values in {braces} are resolved property references — substitute them with the actual values from Step 1. Apply the field resolution from Step 2 to determine which XML elements to use.

Metadata Type Template Reference
CustomApplication configure-metadata-custom-application.md

Execution Note for Step 4: Load and use the doc

  • Agents MUST read the full contents of the references/*.md file referenced in Step 4 before attempting to populate metadata fields.
  • Read the file in full, replace placeholders (e.g. {appName}) with the resolved values, then use the expanded template to populate the metadata XML content.
  • If Step 2 determined the older field names apply, substitute <uiBundle> with <webApplication> in the generated output.

Step 5: Update UI Bundle Meta XML

If Step 2 confirmed the target field exists on UIBundle, add <target>CustomApplication</target> to the .uibundle-meta.xml file (skip if the field doesn't exist in the org's API version):

<?xml version="1.0" encoding="UTF-8"?>
<UIBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <masterLabel>{appName}</masterLabel>
    <description>A Salesforce UI Bundle.</description>
    <isActive>true</isActive>
    <version>1</version>
    <target>CustomApplication</target>
</UIBundle>

Step 6: Do Not Modify Non-Templated Properties

Do not modify any default property values for CustomApplication metadata that are not expressed as variables wrapped in {braces}.

Verification Checklist

Before deploying, confirm:

  • All required properties are resolved
  • API context was queried to determine available fields (Step 2)
  • applications/{appName}.app-meta.xml exists with correct content
  • The bundle reference field matches the org's API version (<uiBundle> or <webApplication>)
  • If target field is supported: .uibundle-meta.xml has <target>CustomApplication</target>
  • Deployment validates successfully:
sf project deploy validate --metadata CustomApplication UIBundle --target-org ${usernameOrAlias}
用于在Salesforce中部署UI Bundle应用。涵盖认证、构建、元数据部署、权限分配、数据导入及GraphQL schema获取与代码生成等全流程,确保部署顺序正确且配置完整。
项目包含 uiBundles/*/src/ 目录或 sfdx-project.json 用户提及部署、推送至Org或部署后设置任务 存在 *.uibundle-meta.xml 文件
skills/experience-ui-bundle-deploy/SKILL.md
npx skills add forcedotcom/sf-skills --skill experience-ui-bundle-deploy -g -y
SKILL.md
Frontmatter
{
    "name": "experience-ui-bundle-deploy",
    "metadata": {
        "version": "1.0"
    },
    "description": "MUST activate when the project contains a uiBundles\/*\/src\/ directory or sfdx-project.json and the task involves deploying, pushing to an org, or post-deploy setup. Use this skill when deploying a UI bundle app to a Salesforce org. Covers the full deployment sequence: org authentication, pre-deploy build, metadata deployment, permission set assignment, data import, GraphQL schema fetch, and codegen. Activate when files like *.uibundle-meta.xml or sfdx-project.json exist and the user mentions deploying, pushing, org setup, or post-deploy tasks."
}

Deploying a UI Bundle

The order of operations is critical when deploying to a Salesforce org. This sequence reflects the canonical flow.

Step 1: Org Authentication

Check if the org is connected. If not, authenticate. All subsequent steps require an authenticated org.

Step 2: Pre-deploy UI Bundle Build

Install dependencies and build the UI bundle to produce dist/. Required before deploying UI bundle entities.

Run when: deploying UI bundles and dist/ is missing or source has changed.

Step 3: Deploy Metadata

Check for a manifest (manifest/package.xml or package.xml) first. If present, deploy using the manifest. If not, deploy all metadata from the project.

Deploys objects, layouts, permission sets, Apex classes, UI bundles, and all other metadata. Must complete before schema fetch — the schema reflects org state.

Step 4: Post-deploy Configuration

Deploying does not mean assigning. After deployment:

  • Permission sets / groups — assign to users so they have access to custom objects and fields. Required for GraphQL introspection to return the correct schema.
  • Profiles — ensure users have the correct profile.
  • Other config — named credentials, connected apps, custom settings, flow activation.

Proactive behavior: after a successful deploy, discover permission sets in force-app/main/default/permissionsets/ and assign each one (or ask the user).

Step 5: Data Import (optional)

Only if data/data-plan.json exists. Delete runs in reverse plan order (children before parents). Import uses Anonymous Apex with duplicate rule save enabled.

Always ask the user before importing or cleaning data.

Step 6: GraphQL Schema and Codegen

  1. Set default org
  2. Fetch schema (GraphQL introspection) — writes schema.graphql at project root
  3. Generate types (codegen reads schema locally)

Run when: schema missing, or metadata/permissions changed since last fetch.

Step 7: Final UI Bundle Build

Build the UI bundle if not already done in Step 2.

Summary: Interaction Order

  1. Check/authenticate org
  2. Build UI bundle (if deploying UI bundles)
  3. Deploy metadata
  4. Assign permissions and configure
  5. Import data (if data plan exists, with user confirmation)
  6. Fetch GraphQL schema and run codegen
  7. Build UI bundle (if needed)

Critical Rules

  • Deploy metadata before fetching schema — custom objects/fields appear only after deployment
  • Assign permissions before schema fetch — the user may lack FLS for custom fields
  • Re-run schema fetch and codegen after every metadata deployment that changes objects, fields, or permissions
  • Never skip permission set assignment or data import silently — either run them or ask the user

Post-deploy Checklist

After every successful metadata deploy:

  1. Discover and assign permission sets (or ask the user)
  2. If data/data-plan.json exists, ask the user about data import
  3. Re-run schema fetch and codegen from the UI bundle directory
用于修改现有UI Bundle应用的视觉和界面,涵盖页面、组件及布局。强制遵循appLayout.tsx结构、shadcn/ui、Tailwind CSS及Salesforce路由规范,确保代码符合项目约定并避免破坏架构。
修改现有UI Bundle应用中的页面、组件或布局样式 项目包含appLayout.tsx、routes.tsx或src/pages/目录时进行视觉变更
skills/experience-ui-bundle-frontend-generate/SKILL.md
npx skills add forcedotcom/sf-skills --skill experience-ui-bundle-frontend-generate -g -y
SKILL.md
Frontmatter
{
    "name": "experience-ui-bundle-frontend-generate",
    "metadata": {
        "version": "1.0"
    },
    "description": "MUST activate before editing ANY file under uiBundles\/*\/src\/ for visual or UI changes to an EXISTING app — pages, components, sections, layout, styling, colors, fonts, navigation, animations, or any look-and-feel change. Use this skill when modifying pages, components, layout, styling, or navigation in an existing UI bundle app. Activate when the project contains appLayout.tsx, routes.tsx, src\/pages\/, src\/components\/, or global.css. This skill contains critical project-specific conventions (appLayout.tsx shell, shadcn\/ui components, Tailwind CSS, Salesforce base-path routing, module restrictions) that override general knowledge. Without this skill, generated code will use wrong imports, break routing, or ignore project structure. Do NOT use when creating a new app from scratch (use experience-ui-bundle-app-coordinate instead)."
}

UI Bundle UI

Identify the Task

Determine which category the request falls into:

Category Examples Implementation Guide
Page New routed page (contacts, dashboard, settings) references/page.md
Header / Footer Site-wide nav bar, footer, branding references/header-footer.md
Component Widget, card, table, form, dialog references/component.md

Layout and Navigation

appLayout.tsx is the source of truth for navigation and layout. Every page shares this shell.

When making any change that affects navigation, header, footer, sidebar, theme, or layout:

  1. Edit src/appLayout.tsx — the layout used by routes.tsx
  2. Replace all default/template nav items and labels with app-specific links and names
  3. Replace placeholder app name everywhere: header, nav brand, footer, <title> in index.html

Before finishing, confirm: Did I update appLayout.tsx with real nav items and branding?

What Where
Layout, nav, branding src/appLayout.tsx
Document title index.html
Root page content Component at root route in routes.tsx

React and TypeScript Standards

Routing

Use a single router package. With createBrowserRouter / RouterProvider, all imports must come from react-router (not react-router-dom).

If the app uses a client-side router (React Router, Remix Router, Vue Router, etc.), always derive basename / basepath / base from the document's <base href> tag at runtime. Never hardcode the basename:

const basename = document.querySelector('base')
  ? new URL(document.querySelector('base').href).pathname.replace(/\/$/, '')
  : '/';
const router = createBrowserRouter(routes, { basename });

Component Library and Styling

  • shadcn/ui for components: import { Button } from '@/components/ui/button';
  • Tailwind CSS utility classes

URL and Path Handling

Apps run behind dynamic base paths. Router navigation (<Link to>, navigate()) uses absolute paths (/x). Non-router attributes (<img src>) use dot-relative (./x). Prefer Vite import for static assets.

TypeScript

  • Never use any — use proper types, generics, or unknown with type guards
  • Event handlers: (event: React.FormEvent<HTMLFormElement>): void
  • State: useState<User | null>(null) — always provide the type parameter
  • No unsafe assertions (obj as User) — use type guards instead

Module Restrictions

React UI bundles must not import Salesforce platform modules like lightning/* or @wire (LWC-only). For data access, use the experience-ui-bundle-salesforce-data-access skill.


Design Thinking

Before coding, commit to a bold aesthetic direction:

  • Purpose: What problem does this interface solve? Who uses it?
  • Tone: Pick a clear direction — brutally minimal, maximalist, retro-futuristic, organic, luxury, playful, editorial, brutalist, art deco, soft/pastel, industrial. Use these as inspiration but design one true to the context.
  • Differentiation: What makes this unforgettable? What's the one thing someone will remember?

Choose a clear conceptual direction and execute it with precision. Bold maximalism and refined minimalism both work — the key is intentionality, not intensity.


Frontend Aesthetics

  • Typography: Choose distinctive, characterful fonts. Pair a display font with a refined body font. Never default to Inter, Roboto, Arial, Space Grotesk, or system fonts.

  • Color: Commit to a cohesive palette using CSS variables. Dominant colors with sharp accents outperform timid, evenly-distributed palettes. Avoid cliched purple gradients on white.

  • Motion: Focus on high-impact moments — one well-orchestrated page load with staggered reveals (animation-delay) creates more delight than scattered micro-interactions. Use scroll-triggering and hover states that surprise. Prefer CSS-only solutions; use Motion library for React when available.

  • Spatial Composition: Unexpected layouts — asymmetry, overlap, diagonal flow, grid-breaking elements. Generous negative space OR controlled density.

  • Backgrounds & Depth: Create atmosphere rather than defaulting to solid colors. Gradient meshes, noise textures, geometric patterns, layered transparencies, dramatic shadows, decorative borders, grain overlays.

  • Mobile Responsiveness: All generated UI MUST be mobile-responsive. Use Tailwind responsive prefixes (sm:, md:, lg:) to adapt layouts across breakpoints. Stack columns on small screens, use flexible grids, and ensure touch targets are at least 44px. Test that navigation, typography, and spacing work on mobile viewports.

Match implementation complexity to the aesthetic vision. Maximalist designs need elaborate animations and effects. Minimalist designs need restraint, precision, and careful spacing/typography. No two designs should look the same — vary themes, fonts, and aesthetics across generations.


Clarifying Questions

Ask one question at a time and stop when you have enough context.

For a Page

  1. Name and purpose?
  2. URL path?
  3. Should it appear in navigation?
  4. Access control? (public, authenticated via PrivateRoute, or unauthenticated via AuthenticationRoute)
  5. Content sections? (list, form, table, detail view)
  6. Data fetching needs?

For a Header / Footer

  1. Header, footer, or both?
  2. Contents? (logo, nav links, user avatar, copyright, social icons)
  3. Sticky header?
  4. Color scheme or style direction?

For a Component

  1. What should it do?
  2. Which page does it belong to?
  3. Shared/reusable or specific to one feature?
  4. Data or props needed?
  5. Internal state? (loading, toggle, form state)
  6. Specific shadcn components to use?

Verification

Before completing, run lint and build from the UI bundle directory. Lint must result in 0 errors and build must succeed.

指导在UI Bundle中通过@salesforce/platform-sdk进行Salesforce数据读写、缓存及GraphQL操作。涵盖查询、变更、刷新及旧版迁移,排除非数据访问场景。
代码导入 @salesforce/platform-sdk 调用 sdk.graphql.query/mutate 或 sdk.fetch 存在 .graphql 文件 需要强制刷新缓存数据 为UI bundle配置数据层以读写Salesforce记录
skills/experience-ui-bundle-salesforce-data-access/SKILL.md
npx skills add forcedotcom/sf-skills --skill experience-ui-bundle-salesforce-data-access -g -y
SKILL.md
Frontmatter
{
    "name": "experience-ui-bundle-salesforce-data-access",
    "metadata": {
        "version": "2.1"
    },
    "description": "MUST activate when a uiBundles\/*\/src\/ project does ANY Salesforce record operation — reading, creating, updating, deleting, or caching\/refreshing query results. Triggers: code importing @salesforce\/platform-sdk, calls to sdk.graphql.query \/ sdk.graphql.mutate \/ sdk.fetch, *.graphql files, stale data needing a force-refresh, or wiring up a UI bundle's data layer to read, write, or refresh Salesforce records. The default for new read\/write work is the Read\/Write workflow with the current @salesforce\/platform-sdk API; only follow the migration path when EXISTING code already uses the old @salesforce\/sdk-data callable form. Not for building app shell\/UI, styling, file upload, or auth\/search scaffolding — use the other ui-bundle-* skills. DO NOT TRIGGER when: OAuth setup, schema changes, Bulk\/Tooling\/Metadata API, or declarative automation."
}

Salesforce Data Access (UI bundles)

All Salesforce data access in a UI bundle goes through the @salesforce/platform-sdk data SDK. The SDK handles auth, CSRF, and base-URL resolution, and — on the WebApp surface — caches every GraphQL query by default.

This file is the workflow + guardrail spine. Depth lives in linked docs:

  • references/graphiti-cli.md — the graphiti CLI (sf-gql-* commands) that compiles a small JSON spec into a schema-correct, guardrail-applied query + variables + types. The preferred way to author the GraphQL in steps below; falls back to the schema-grep script when unavailable.
  • references/sdk-api.md — the new call API: query/mutate, QueryResult, typing, error-handling stances.
  • references/caching.md — on-by-default cache + the two refresh modes (result.refresh/subscribe vs per-call cacheControl).
  • references/graphql-hand-authoring.md — schema lookup, read / mutation templates, every platform guardrail (@optional, pagination, limits, semi-join, wrappers, error table…).
  • references/rest-and-integration.mdsdk.fetch, the supported-API allowlist, and the reactive/lifecycle integration patterns.
  • references/migration.md — old @salesforce/sdk-data callable code → new namespace. The only place the dead API appears as usable code.

The one-paragraph mental model

const sdk = await createDataSDK(). Then sdk.graphql is a namespace, not a function: sdk.graphql!.query({...}) for reads, sdk.graphql!.mutate({...}) for writes. On WebApp, every query() is cached by default (300s). HTTP 200 never means success — always check result.errors. Verify every entity and field against the schema before you query it: one unverified field fails the whole query at runtime, and schema.graphql is too large to eyeball — look it up.

import { createDataSDK, gql } from "@salesforce/platform-sdk"; // gql tags the query string so codegen + eslint validate it

const sdk = await createDataSDK();
const result = await sdk.graphql!.query({ query: GET_ACCOUNTS, variables });
if (result.errors?.length) throw new Error(result.errors.map((e) => e.message).join("; "));
const rows = result.data?.uiapi?.query?.Account?.edges?.map((e) => e.node) ?? []; // unwrap edges/node; read field values via .value

Typed call params (query<GetAccountsQuery, GetAccountsQueryVariables>), the CacheControl type, and NodeOfConnection<T> (extracts a node type from a Connection for clean typing) all live in references/sdk-api.md.

This changed (breaking — PR #502). The previous callable sdk.graphql(...) form and the previous package name are dead — the code above is the only correct form. If you encounter the old API in existing code (or a stale dist/ artifact), don't copy it; convert it per Working on existing code.

sdk.graphql! is WebApp-only. The non-null assertion above is correct only if the bundle runs solely on WebApp. On other surfaces it can crash — decide before you write it. See Surfaces — ! vs guard below.


Surfaces — sdk.graphql! vs guard

createDataSDK() runs on multiple surfaces, and sdk.graphql / sdk.fetch are genuinely optional (typed graphql?: …). Whether you may assert them with ! depends entirely on where the bundle runs — this is the one surface decision that turns into a runtime crash if you get it wrong, so make it explicitly before writing any query/mutate call:

Surface(s) sdk.graphql Write
WebApp only always present sdk.graphql!.query({...})! is safe; every shipped WebApp consumer uses it
Mosaic / OpenAI / MCPApps (or any bundle that might run off-WebApp) can be undefined guard first (if (!sdk.graphql) return …), then call

Rule of thumb: if you cannot prove the bundle is WebApp-only, guard. A bare sdk.graphql! that later ships to another surface throws Cannot read properties of undefined at runtime — TypeScript won't catch it because ! silences exactly that check (same applies to sdk.fetch!). The portable guard snippet lives in references/sdk-api.md.


Step 0 — Route the task

The task is… Go to
Read records Read workflow below
Create / update / delete records Write workflow below
Object/field metadata, picklist values, related-list metadata, aggregations Beyond record CRUD below
Data is stale / "add a refresh button" / "cache it longer" Freshness & caching below
Something GraphQL can't express (Apex REST, file upload, Einstein) references/rest-and-integration.md
Migrating old sdk.graphql?.(query, vars) code Working on existing code below

GraphQL covers far more than record reads and writes — prefer it for anything the uiapi namespace exposes (see Beyond record CRUD). Reach for REST only when the data genuinely lives outside uiapi (Apex REST, file upload, Einstein) — see references/rest-and-integration.md.


Preconditions — verify before writing any query

<skill-dir> below is wherever this skill is installed (the directory this SKILL.md loaded from). The schema-lookup script ships inside it. The script does not hunt for schema.graphql by walking up the tree — an ancestor schema can belong to a different org and would validate fields against the wrong one. Resolve the schema explicitly: run from the SFDX project root (where schema.graphql lives), or pass --schema <path> / set GRAPHQL_SCHEMA=<path>. The script echoes the schema it resolved ([graphql-search] using schema: … on stderr) — glance at it to confirm you grounded against the right file.

# Requirement Verify If missing
1 @salesforce/platform-sdk installed package.json in the UI bundle dir Tell user to install it; cannot proceed
2 A grounding tool resolves Preferred: npx graphiti sf-gql-discover '{"org":"<alias>","mode":"list_objects"}' from the UI bundle dir returns objects. Fallback: bash <skill-dir>/scripts/graphql-search.sh <Entity> from the project root prints a lookup, not "schema.graphql not found" No graphiti dep / org won't prime → use the script. Script can't find schema.graphql → pass --schema <path>, or npm run graphql:schema from the UI bundle dir. (references/graphiti-cli.md covers CLI setup)
3 Target objects/fields deployed The object appears in sf-gql-discover (or graphql-search.sh <Entity> returns output) Entity absent usually means it isn't deployed (or the cache/schema is stale). Refresh: npx graphiti sf-gql-connect '{"org":"<alias>","forceRefresh":true}' (CLI) or npm run graphql:schema (script). If still absent, deploy the metadata (the platform-metadata-deploy skill handles this) and assign the permission sets, then re-check

If preconditions aren't met you may still scaffold components, routes, and layout — but use empty arrays / null for data, mark query sites with // TODO: add query after schema verification, and add a plan item to return. Do not write GraphQL strings until the schema workflow is complete.


Read workflow

  1. Look up the schema first — never guess a name. Preferred (graphiti): when the exact API name is at all uncertain, list before you describenpx graphiti sf-gql-discover '{"org":"<alias>","mode":"list_objects","search":"<intent>"}' to find the real name, then npx graphiti sf-gql-discover '{"org":"<alias>","mode":"describe_object","object":"<Entity>"}' for exact field/type names, picklist values, filterable/sortable. An empty list or missing object is a fact about the org (wrong name or not deployed), not a tool failure — re-list or forceRefresh; do not fall back to the script for this (see guardrail 2). Fallback is only for a CLI that genuinely can't run (no graphiti dep / org won't prime): bash <skill-dir>/scripts/graphql-search.sh <Entity> from the SFDX project root. (Full rules: references/graphql-hand-authoring.md.)

  2. Write the query. Preferred — compile it with graphiti: npx graphiti sf-gql-list '{"org":"<alias>","object":"<Entity>","fields":[…],"first":N}' returns a { query, variables, types, warnings } envelope with @optional, value/displayValue, edges/node, and first:/pageInfo already applied. Confirm warnings: [] (a non-empty array means the object wasn't in the primed schema — the query is degraded; don't ship it), then paste the query verbatim into inline gql (simple) or an external .graphql file (one operation per file, imported with the bundler's ?raw suffix — import Q from "./q.graphql?raw" brings the file in as a plain string). Fallback — hand-author: apply @optional to every selectable FLS-gated field — scalar leaf fields (Name @optional { value }) and parent/child relationships and the fields inside them — but NOT on Id, on connection plumbing (edges, node, the connection field itself), or on pageInfo; the graphiti output leaves those bare and is the canonical placement. Always set first:, include pageInfo if it may page. Either way, full mechanics and the primed-vs-degraded behavior: references/graphiti-cli.md.

  3. Generate typesnpm run graphql:codegen (from the UI bundle dir) → src/api/graphql-operations-types.ts.

  4. Call query() with the generated types:

    import type { GetAccountsQuery, GetAccountsQueryVariables } from "../graphql-operations-types";
    
    const result = await sdk.graphql!.query<GetAccountsQuery, GetAccountsQueryVariables>({
      query: GET_ACCOUNTS,
      variables: { first: 20 },
      // cacheControl,            // optional — see Freshness & caching
    });
    
  5. Handle the result. result.data + result.errors are the initial snapshot; result.subscribe / result.refresh are the reactive handles. Always check errors before reading data:

    if (result.errors?.length) throw new Error(result.errors.map((e) => e.message).join("; "));
    const rows = result.data?.uiapi?.query?.Account?.edges?.map((e) => e.node) ?? [];
    

Defend consuming code with ?./?? (because @optional can omit fields). Error-handling stances (strict / tolerant / discriminated) and NodeOfConnection typing: references/sdk-api.md.


Write workflow

1–3 as above (schema lookup → write the mutation → codegen). To compile the mutation with graphiti, use sf-gql-create / sf-gql-update / sf-gql-delete — they emit the uiapi { <Object>Create(input: $input) { Record {…} } } shape; the types field tells you the input shape. Details: references/graphiti-cli.md. 4. Call mutate() — note the option key is mutation, not query, and that mutations are never cached. The runtime variables shape differs per operation — values are raw (never {value}-wrapped; that wrapper is a read-shape thing and breaks writes) and nest under the entity key:

// create — input.<Entity> holds the new field values
variables: { input: { Account: { Name: "Acme", Industry: "Technology" } } }
// update — sibling Id alongside the entity key
variables: { input: { Id: "001…", Account: { Industry: "Finance" } } }
// delete — Id only, no entity key (generic RecordDeleteInput)
variables: { input: { Id: "001…" } }

const { data, errors } = await sdk.graphql!.mutate<CreateAccountMutation, CreateAccountMutationVariables>({
  mutation: CREATE_ACCOUNT,
  variables: { input: { Account: { Name: "Acme" } } },
});
if (errors?.length) throw new Error(errors.map((e) => e.message).join("; "));

This is the variables shape the spine owns; the CLI types-field interpretation is in references/graphiti-cli.md and the GraphQL-document field constraints (createable/updateable, ApiName references, @{alias} chaining) in references/graphql-hand-authoring.md. 5. Re-freshen affected reads. mutate() has no refresh. To update a live list after a write, hold the QueryResult from your earlier query() call (e.g. accountsResult) and call await accountsResult.refresh() (forced re-fetch, pushes to subscribers) — note this is the read's handle, not anything mutate() returns. See Freshness & caching.

Mutation syntax is exacting: wrap under uiapi(input: { allOrNone: ... }), only createable/updateable fields, Create/Update output is always Record but Delete has no Record field — select Id only. Full template + chaining + constraints: references/graphql-hand-authoring.md.


Beyond record CRUD

The uiapi namespace is not just record reads/writes. Before reaching for REST, check whether GraphQL already covers it — the same sdk.graphql!.query() call, different sub-selection. The top-level uiapi fields:

Need Use Returns
Query records uiapi { query { <Entity>(...) } } records (the Read workflow)
Counts / sums / grouped rollups without pulling rows uiapi { aggregate { <Entity>(groupBy: …) } } aggregated buckets
Object/field metadata — labels, data types, createable/updateable, record types uiapi { objectInfos(apiNames: […]) } ObjectInfo[]
Picklist values (per record type) uiapi { objectInfos(objectInfoInputs: […]) { fields … on PicklistField { … } } } picklist values
Related-list metadata — display columns, ordering for a parent's related list uiapi { relatedListByName(parentApiName, relatedListName) } RelatedListInfo

Same rules as record reads: verify every type/field first, @optional where FLS applies, check result.errors. Aggregations can be compiled with npx graphiti sf-gql-aggregate (pass groupBy + aggregations); object metadata / picklists / related lists are hand-authored — templates: references/graphql-hand-authoring.md.

Two related capabilities (the current-user record and layout delivery) need confirmation against a current org schema before this skill documents a query shape — tracked as a follow-up, not yet covered here.


Freshness & caching

Caching is ON by default on WebApp. Every sdk.graphql!.query() is cached with a 300-second max-age TTL — no opt-in flag, no factory, no import subpath. Do not build your own cache (no React Query, SWR, localStorage, or hand-rolled Map). The cache is shared across SDK instances by baseUrl: the same query+variables from a different createDataSDK() targeting the same host is a cache hit. Only non-empty, error-free data is cached. mutate() is never cached.

There are two distinct freshness tools — keep them separate:

  1. Per-call cacheControl — a one-shot policy override on the query options bag ("no-cache" / "only-if-cached" / { type: "max-age", maxAge: <seconds> }). The type and exact per-value behavior live in references/sdk-api.md. Take cacheControl as an optional param on the read function and expose each distinct policy as a thin named export in the same data-layer file — a "call site" is a named export, not a new React component. For getAccounts(first, after?, cacheControl?): export const refreshAccounts = () => getAccounts(20, undefined, "no-cache") (and likewise offlineAccounts"only-if-cached", shortLivedAccounts{ type: "max-age", maxAge: 10 }). Keep the policy in the data layer.
  2. Reactive subscribe / refresh — a stateful handle on a live QueryResult: result.subscribe(cb) fires on every later snapshot, result.refresh() re-fetches bypassing the cache and pushes to subscribers. Shape in references/sdk-api.md; subscription lifecycle (always unsubscribe on teardown) in references/caching.md.
Want Reach for
Freshness within ~5 min is fine nothing (default cache)
This one read must bypass the cache (refresh button) cacheControl: "no-cache"
Read only cached data, tolerate misses (offline-first) cacheControl: "only-if-cached" — a miss is expected, not an error: it surfaces a DataNotFoundError on result.errors (no network, no throw). Check result.errors, render empty state, do not throw and do not fall back to the network — that defeats offline-first.
Tighter/looser TTL for this query cacheControl: { type: "max-age", maxAge: 60 } (maxAge is in seconds)
Mounted component reflects updates over time result.subscribe(cb)
Re-fetch now + notify all subscribers (e.g. after a mutation) result.refresh()

cacheControl is fire-and-forget at call time; subscribe/refresh is a live handle. Different mechanisms, different jobs — don't conflate "refresh" with "no-cache". Full behavior, the reactive-subscription lifecycle, and uncached-surface caveats: references/caching.md.


Working on existing code (migration)

Only enter this path if the existing code actually uses the old API — i.e. it imports @salesforce/sdk-data or calls the callable sdk.graphql(query, vars) form. For any new read/write, ignore migration entirely and use the Read workflow / Write workflow — those already show the only correct API.

When you do have old code to convert, see references/migration.md for the before→after diff (imports, query/mutate calls, optional-chaining → non-null assertion, codegen type placement) and a checklist. The target API is exactly what the Read/Write workflows above prescribe — migrating is just swapping the old form for that.


Platform guardrails — never regress these

These are Salesforce GraphQL platform behaviors, independent of the SDK. Violations cause silent runtime failures. (Details + templates: references/graphql-hand-authoring.md.)

  1. HTTP 200 ≠ success — always parse result.errors; the Promise resolves even on failure.
  2. Schema is the only source of truth — verify, never invent. Verify every entity/field/type via graphiti sf-gql-discover (preferred) or bash <skill-dir>/scripts/graphql-search.sh <Entity> before use. Case-sensitive; __c/__e; _Record entity suffix (v60+). When graphiti is primed, a "not found"/empty/Cannot query field answer (including from graphql-codegen/@graphql-eslint, even when the message points at schema.graphql) is a fact about the org — wrong name or undeployed/inaccessible metadata, not a tool failure: fix the operation, or deploy the metadata (the platform-metadata-deploy skill)
    • assign perms + refresh (sf-gql-connect --forceRefresh / npm run graphql:schema). Do not fall back to the script, hand-author around it, or guess a name — a guessed entity or field silently fails the whole query at runtime; if lookups aren't converging, ask the user rather than keep spiraling. schema.graphql and the codegen output (src/api/graphql-operations-types.ts) are read-only generated mirrors — never open or edit them (honor any # DO NOT EDIT marker). Hand-adding a missing type satisfies codegen/lint but grants no org access; it just hides the failure until runtime. Fall back to the script only when the CLI can't run at all (no dep / SCHEMA_PRIME_FAILED).
  3. @optional on every FLS-gated field at each nesting level — scalar leaf fields plus each parent/child relationship and the fields inside it (FLS fails the whole query otherwise, v65+). Do NOT decorate Id, the connection plumbing (edges, node, the connection field), or pageInfo — those are not FLS-gated and the graphiti output leaves them bare. Consume with ?./??. Placement rules: references/graphql-hand-authoring.md.
  4. Mutations wrap under uiapi(input: { allOrNone: ... }); set allOrNone explicitly; output excludes child/navigated-reference fields; the output field is literally named Record (unrelated to the _Record entity suffix in rule 2) — Delete → Id only. GA v66+.
  5. Explicit pagination — always set first:, because the server silently caps at 10 and you'll drop rows with no error; forward-only (first/after, no last/before); upperBound (v59+) raises the per-request ceiling for large sets (when set, first must be 200–2000).
  6. SOQL governor limits applyuiapi queries compile to SOQL, so the same governor limits are inherited: ≤10 subqueries, ≤5 child→parent levels, ≤1 parent→child level, ≤2,000 records/subquery. Split into multiple requests if you'd exceed them.
  7. Field value wrappers — read the raw value via .value; displayValue is the server-formatted string for UI. When a field is both shown and operated on (currency, dates, picklists), select both value and displayValue so you don't reformat on the client. Display-only fields can take just displayValue.
  8. Compound fields — filter/order on constituents (BillingCity), not the wrapper (BillingAddress).
  9. Supported APIs only — GraphQL (uiapi), UI API REST, Apex REST, Connect REST, Einstein LLM via sdk.fetch. NOT: Enterprise SOQL /query, Aura-enabled Apex, Chatter (use uiapi.currentUser). See references/rest-and-integration.md.

One SDK convention lives in the workflows, not this list (it's not a platform behavior): always run npm run graphql:codegen and use the generated types after writing an operation (Read workflow step 3). Also in the Pre-flight checklist.

graphiti applies most of these for you. When you compile a query with sf-gql-* against an object that's in the primed schema, rules 3 (@optional), 4 (mutation Record output envelope and entity-keyed input — not allOrNone, which you still add yourself), 5 (first:/pageInfo), and 7 (value/displayValue wrappers) come out already satisfied — which is exactly why you paste the query verbatim rather than re-deriving it. Rules 1 (check result.errors), 6 (governor limits), 8 (compound fields), and 9 (supported APIs) are still on you. And the automation only fires when the object is primed: a non-empty warnings array means it isn't, and the emitted query is degraded (bare fields, no guardrails) — see references/graphiti-cli.md.


Commands & layout

<skill-dir>/                            ← wherever this skill is installed
└── scripts/graphql-search.sh           ← schema lookup (ships with the skill)

<project-root>/                         ← SFDX project root; run the script from here
├── schema.graphql                      ← generated mirror; grep target (never open or edit; script reads ./schema.graphql)
└── force-app/main/default/uiBundles/<app>/   ← UI bundle dir
    ├── package.json                    ← npm scripts
    └── src/api/                        ← queries, generated types, SDK calls
Command Run from Purpose
npx graphiti sf-gql-discover '{…}' UI bundle dir Discover objects/fields against the live org (preferred grounding)
npx graphiti sf-gql-<list|detail|aggregate|create|update|delete|raw> '{…}' UI bundle dir Compile a guardrail-applied query/mutation (references/graphiti-cli.md)
npx graphiti sf-gql-connect '{"org":"<alias>","forceRefresh":true}' UI bundle dir Refresh graphiti's schema cache after a deploy
bash <skill-dir>/scripts/graphql-search.sh <Entity> project root (or pass --schema <path>; no tree walk-up) Schema lookup fallback (grep over local schema.graphql)
npm run graphql:schema UI bundle dir Fetch/refresh schema.graphql (for the fallback script)
npm run graphql:codegen UI bundle dir Generate operation types
npx eslint <file> UI bundle dir Lint (catches gql schema violations)

Pre-flight checklist

  • Surface decided: sdk.graphql! only if WebApp-only; otherwise guard with if (!sdk.graphql) … (Surfaces)
  • Every field/entity verified — sf-gql-discover (preferred) or graphql-search.sh (fallback, against the right schema)
  • If compiled with graphiti: warnings: [] confirmed (non-empty = degraded query, don't ship); query pasted verbatim
  • @optional on FLS-gated fields + relationships (NOT Id/edges/node/pageInfo); ?./?? in consuming code
  • result.errors checked before reading result.data
  • Caching considered: default 300s OK, or cacheControl / refresh chosen deliberately
  • npm run graphql:codegen run; generated types used; npx eslint passes
用于生成Salesforce架构、OAuth流程、ERD等文本型Mermaid图表及ASCII备用图。适用于文档集成,不处理图片输出或非Salesforce系统。
用户请求绘制架构图、流程图或序列图 用户提到'ERD'、'diagram'或'visualize' 需要Markdown兼容的文本可视化
skills/external-diagram-mermaid-generate/SKILL.md
npx skills add forcedotcom/sf-skills --skill external-diagram-mermaid-generate -g -y
SKILL.md
Frontmatter
{
    "name": "external-diagram-mermaid-generate",
    "metadata": {
        "version": "1.0"
    },
    "description": "Salesforce architecture diagrams using Mermaid with ASCII fallback. Use this skill when generating text-based diagrams for Salesforce architecture, OAuth flows, ERDs, integration sequences, or Agentforce structure. TRIGGER when: user says \"diagram\", \"visualize\", \"ERD\", or asks for sequence diagrams, flowcharts, class diagrams, or architecture visualizations in Mermaid. DO NOT TRIGGER when: user wants PNG\/SVG image output (use external-diagram-visual-generate), or asks about non-Salesforce systems.",
    "compatibility": "Requires Mermaid-capable renderer for diagram previews"
}

external-diagram-mermaid-generate: Salesforce Diagram Generation

Use this skill when the user needs text-based diagrams: Mermaid diagrams for architecture, OAuth, integration flows, ERDs, or Agentforce structure, plus ASCII fallback when plain-text compatibility matters.

Scope

In Scope

Use external-diagram-mermaid-generate when the user wants:

  • Mermaid output
  • ASCII fallback diagrams
  • architecture, sequence, flowchart, or ERD views in markdown-friendly form
  • diagrams that can live directly in docs, READMEs, or issues

Out of Scope — Delegate elsewhere when the user wants:


Supported Diagram Families

Type Preferred Mermaid form Typical use
OAuth / auth flows sequenceDiagram Authorization Code, JWT, PKCE, Device Flow
ERD / data model flowchart LR object relationships and sharing context
integration sequence sequenceDiagram request/response or event choreography
system landscape flowchart high-level architecture
role / access hierarchy flowchart users, profiles, permissions
Agentforce behavior map flowchart agent → topic → action relationships

Required Context to Gather First

Ask for or infer:

  • diagram type
  • scope and entities / systems involved
  • output preference: Mermaid only, ASCII only, or both
  • whether styling should be minimal, documentation-first, or presentation-friendly
  • for ERDs: whether org metadata is available for grounding

Recommended Workflow

1. Pick the right diagram structure

  • use sequenceDiagram for time-ordered interactions
  • use flowchart LR for ERDs and capability maps
  • keep a single primary story per diagram when possible

2. Gather data

For ERDs and grounded diagrams:

3. Generate Mermaid first

Apply:

  • accurate labels
  • simple readable node text
  • consistent relationship notation
  • restrained styling that renders cleanly in markdown viewers

4. Add ASCII fallback when useful

Provide an ASCII version when the user wants terminal compatibility or plaintext documentation.

5. Explain the diagram briefly

Call out the key relationships, flow direction, and any assumptions.


High-Signal Rules

For sequence diagrams

  • use autonumber when step order matters
  • distinguish requests vs responses clearly
  • use notes sparingly for protocol detail

For ERDs

  • prefer flowchart LR
  • keep object cards simple
  • use clear relationship arrows
  • avoid field overload unless the user explicitly asks for field-level detail
  • color-code object types only when it improves readability

For ASCII output

  • keep width reasonable
  • align arrows and boxes consistently
  • optimize for readability over decoration

Output Format

## <Diagram Title>

### Mermaid Diagram
```mermaid
<diagram>
```

### ASCII Fallback
```text
<ascii>
```

### Notes
- <key point>
- <assumption or limitation>

Cross-Skill Integration

Need Delegate to Reason
real object / field definitions platform-custom-object-generate / platform-custom-field-generate grounded ERD generation
rendered diagram / image output external-diagram-visual-generate visual polish beyond Mermaid
connected-app auth setup context integration-connectivity-connected-app-configure accurate OAuth flows
Agentforce logic visualization agentforce-generate source-of-truth behavior details
Flow behavior diagrams automation-flow-generate actual Flow logic grounding

Gotchas

Issue Resolution
Mermaid renderer not available Provide ASCII fallback automatically; note that the Mermaid block still carries the diagram for copy-paste into a renderer
ERD becomes unreadable with too many objects Split into sub-diagrams by domain (Sales, Service, etc.) and link them in prose
Sequence diagram step order unclear Use autonumber directive to make step ordering explicit
OAuth flow actors differ by grant type Read the relevant asset template first before generating to avoid actor mismatch

Reference File Index

Conventions & rules — read before generating

Styling

Preview

OAuth flow templates — load the matching template when generating OAuth diagrams

Data model ERD templates — load the matching template when generating ERDs

Other diagram templates


Output Expectations

Deliverables produced by this skill for each request:

  • Mermaid code block — fenced ```mermaid block ready to paste into GitHub, Confluence, or any Mermaid-capable renderer
  • ASCII fallback (when requested or when Mermaid renderer is unavailable) — text-only diagram using box/arrow characters
  • Brief explanation — 2-5 bullet points calling out key relationships, flow direction, and any assumptions or limitations in the diagram
  • For ERDs: object cards with field labels and relationship type annotations
  • For sequence diagrams: numbered steps (autonumber) with clear actor labels

Score Guide

Score Meaning
72–80 production-ready diagram
60–71 clear and useful with minor polish left
48–59 functional but could be clearer
35–47 needs structural improvement
< 35 inaccurate or incomplete
用于配置Salesforce Connected Apps和External Client Apps的OAuth设置,涵盖JWT、PKCE及流程选择。适用于涉及相关XML文件或需要决定应用架构的场景,排除Named Credentials等其它集成任务。
用户配置Connected Apps或External Client Apps (ECA) 设置OAuth流(如JWT Bearer、PKCE) 处理.connectedApp-meta.xml或.eca-meta.xml文件
skills/integration-connectivity-connected-app-configure/SKILL.md
npx skills add forcedotcom/sf-skills --skill integration-connectivity-connected-app-configure -g -y
SKILL.md
Frontmatter
{
    "name": "integration-connectivity-connected-app-configure",
    "metadata": {
        "version": "1.1"
    },
    "description": "Salesforce Connected Apps and External Client Apps OAuth configuration with 120-point scoring. Use this skill to configure OAuth flows, JWT bearer auth, Connected Apps, and External Client Apps in Salesforce. TRIGGER when: user configures OAuth flows, JWT bearer auth, Connected Apps, ECAs, or touches .connectedApp-meta.xml \/ .eca-meta.xml files. DO NOT TRIGGER when: configuring Named Credentials for callouts (use integration-connectivity-generate), reviewing permission policies (use platform-metadata-deploy), or writing Apex token-handling code (use platform-apex-generate).",
    "allowed-tools": "Bash Read Write Edit Glob Grep WebFetch AskUserQuestion TodoWrite"
}

integration-connectivity-connected-app-configure: Salesforce Connected Apps & External Client Apps

Use this skill when the user needs OAuth app configuration in Salesforce: Connected Apps, External Client Apps (ECAs), JWT bearer setup, PKCE decisions, scope design, or migration from older Connected App patterns to newer ECA patterns.

Scope

In scope:

  • .connectedApp-meta.xml or .eca-meta.xml files
  • OAuth flow selection and callback / scope setup
  • JWT bearer auth, device flow, client credentials, or auth-code decisions
  • Connected App vs External Client App architecture choices
  • Consumer key / secret / certificate handling strategy

Out of scope — delegate elsewhere:


First Decision: Connected App or External Client App

If the need is... Prefer
simple single-org OAuth app Connected App
new development with better secret handling External Client App
multi-org / packaging / stronger operational controls External Client App
straightforward legacy compatibility Connected App

Default guidance:

  • Choose ECA for new regulated, packageable, or automation-heavy solutions.
  • Choose Connected App when simplicity and legacy compatibility matter more.
  • Spring '26 note: creation of new Connected Apps is disabled by default in orgs. For new integrations, prefer External Client Apps unless Connected App compatibility is explicitly required.

Required Inputs

Ask for or infer:

  • App type: Connected App or ECA
  • OAuth flow: auth code, PKCE, JWT bearer, device, client credentials
  • Client type: confidential vs public
  • Callback URLs / redirect surfaces
  • Required scopes
  • Distribution model: local org only vs packageable / multi-org
  • Whether certificates or secret rotation are required

Workflow

1. Choose the app model

Decide whether a Connected App or ECA is the better long-term fit using the decision table above.

2. Choose the OAuth flow

Use case Default flow
backend web app Authorization Code
SPA / mobile / public client Authorization Code + PKCE
server-to-server / CI/CD JWT Bearer
device / CLI auth Device Flow
service account style app Client Credentials (typically ECA)

3. Start from the right template

Read the appropriate template before generating — do not build from scratch:

Template Use case
assets/connected-app-basic.xml Simple API integration, minimal OAuth
assets/connected-app-oauth.xml Web app with full OAuth 2.0 configuration
assets/connected-app-jwt.xml JWT bearer / server-to-server
assets/connected-app-canvas.xml Embedding external apps in Salesforce UI (Canvas)
assets/external-client-app.xml ECA header file — all new ECA builds start here
assets/eca-global-oauth.xml ECA global OAuth settings (scopes, PKCE, rotation)
assets/eca-oauth-settings.xml ECA per-app OAuth settings
assets/eca-policies.xml ECA configurable policies

If you need source-controlled ECA OAuth security metadata, retrieve it from an org first and treat the retrieved file as the schema source of truth:

sf project retrieve start --metadata ExtlClntAppOauthSecuritySettings:<AppName> --target-org <alias>

4. Apply security hardening

Read references/security-checklist.md for the full 120-point security checklist. Favor:

  • Least-privilege scopes
  • Explicit callback URLs
  • PKCE for public clients
  • Certificate-based auth where appropriate
  • Rotation-ready secret / key handling
  • IP restrictions when realistic and maintainable

5. Validate deployment readiness

Read references/testing-validation-guide.md before handoff. Confirm:

  • Metadata file naming is correct (see Gotchas below)
  • Scopes are justified
  • Callback and auth model match the real client type
  • Secrets are not embedded in source

6. Handle errors

If deployment fails, check the error output for:

  • DUPLICATE_VALUE — a Connected App or ECA with this name already exists; rename or retrieve-then-update instead
  • INVALID_CROSS_REFERENCE_KEY — the externalClientApplication name in an ECA settings file doesn't match the .eca-meta.xml filename exactly
  • INSUFFICIENT_ACCESS_OR_READONLY — user lacks the "Manage Connected Apps" permission
  • If any step fails, do not proceed to the next step — surface the error to the user with the specific message above

Rules / Constraints

Rule Rationale
Never commit consumer secrets to source control Credential exposure risk
Never use Full scope by default Unnecessary privilege; request only what the app needs
Always use PKCE for public clients (mobile, SPA) Prevents auth code interception
Never use wildcard or overly broad callback URLs Token interception risk
ECA OAuth security settings must be retrieved from org before editing File schema is not fully documented; retrieve-first ensures accuracy
Use <alias> placeholders in CLI commands, never hardcoded org URLs Org URLs vary per environment
Detect actual packageDirectory from sfdx-project.json before writing files Projects may not use the default force-app/main/default/ layout

Metadata Notes That Matter

Connected App

Default source location (verify via sfdx-project.json → packageDirectories):

  • <packageDir>/connectedApps/

External Client App

ECA metadata spans multiple top-level source directories. Default locations (verify via sfdx-project.json):

Directory Metadata type File suffix
<packageDir>/externalClientApps/ ExternalClientApplication .eca-meta.xml
<packageDir>/extlClntAppGlobalOauthSets/ ExtlClntAppGlobalOauthSettings .ecaGlblOauth-meta.xml
<packageDir>/extlClntAppOauthSettings/ ExtlClntAppOauthSettings .ecaOauth-meta.xml
<packageDir>/extlClntAppOauthSecuritySettings/ ExtlClntAppOauthSecuritySettings .ecaOauthSecurity-meta.xml
<packageDir>/extlClntAppOauthPolicies/ ExtlClntAppOauthConfigurablePolicies .ecaOauthPlcy-meta.xml
<packageDir>/extlClntAppPolicies/ ExtlClntAppConfigurablePolicies .ecaPlcy-meta.xml

Gotchas

Gotcha Detail
.ecaGlblOauth not .ecaGlobalOauth The global OAuth suffix is abbreviated — using the long form will break deployment
.ecaPlcy not .ecaPolicy Same abbreviation pattern — the general policy suffix is short form
.ecaOauthSecurity for security settings Use .ecaOauthSecurity, not .ecaSecurity
ECA OAuth security settings are retrieve-only Cannot be created from scratch in source — always retrieve from org first
Spring '26: new Connected Apps disabled by default New orgs block Connected App creation; use ECA unless explicitly required
Consumer key is generated post-deploy You cannot set the consumer key in metadata — retrieve it after first deployment

Output Expectations

When finishing, confirm and report in this order:

  1. App type chosen — Connected App or External Client App
  2. OAuth flow chosen
  3. Files created or updated — list each metadata file path
  4. Security decisions — scopes, PKCE, certs, secrets, IP policy
  5. Next deployment / testing step

Suggested output shape:

App: <name>
Type: Connected App | External Client App
Flow: <oauth flow>
Files: <paths>
Security: <scopes, PKCE, certs, secrets, IP policy>
Next step: <deploy, retrieve consumer key, or test auth flow>
Score: <x>/120

Cross-Skill Integration

Need Delegate to Reason
Named Credential / callout runtime config integration-connectivity-generate runtime integration setup
Deploy app metadata platform-metadata-deploy org validation and deployment
Apex token or refresh handling platform-apex-generate implementation logic

Score Guide

Score Meaning
80+ production-ready OAuth app config
54–79 workable but needs hardening review
< 54 block deployment until fixed

Reference File Index

File When to read
assets/connected-app-basic.xml Step 3 — template for simple Connected App with minimal OAuth
assets/connected-app-oauth.xml Step 3 — template for full OAuth 2.0 Connected App
assets/connected-app-jwt.xml Step 3 — template for JWT bearer / server-to-server Connected App
assets/connected-app-canvas.xml Step 3 — template for Canvas app embedding in Salesforce UI
assets/external-client-app.xml Step 3 — ECA header file template
assets/eca-global-oauth.xml Step 3 — ECA global OAuth settings template (PKCE, rotation, callbacks)
assets/eca-oauth-settings.xml Step 3 — ECA per-app OAuth settings template
assets/eca-policies.xml Step 3 — ECA configurable policies template
references/oauth-flows-reference.md Step 2 — detailed OAuth flow comparison and decision guide
references/security-checklist.md Step 4 — full 120-point security scoring checklist
references/testing-validation-guide.md Step 5 — pre-deployment validation and testing guide
references/migration-guide.md When migrating from Connected App to ECA patterns
references/example-usage.md Full end-to-end examples for common OAuth scenarios
负责Salesforce集成架构与运行时配置,涵盖命名凭证、外部服务、REST/SOAP调用、平台事件及CDC。适用于设置集成模式与安全认证,排除OAuth应用配置、纯Apex逻辑及数据导入导出任务。
用户设置Named Credentials或External Services 配置REST/SOAP调用或Platform Events 处理CDC或事件驱动集成设计
skills/integration-connectivity-generate/SKILL.md
npx skills add forcedotcom/sf-skills --skill integration-connectivity-generate -g -y
SKILL.md
Frontmatter
{
    "name": "integration-connectivity-generate",
    "metadata": {
        "version": "1.1"
    },
    "description": "Salesforce integration architecture and runtime plumbing with 120-point scoring. Use this skill to set up Named Credentials, External Credentials, External Services, REST\/SOAP callout patterns, Platform Events, and Change Data Capture. TRIGGER when: user sets up Named Credentials, External Services, REST\/SOAP callouts, Platform Events, CDC, or touches .namedCredential-meta.xml files. DO NOT TRIGGER when: Connected App\/OAuth config (use integration-connectivity-connected-app-configure), Apex-only logic (use platform-apex-generate), data import\/export (use platform-data-manage), or CDC channel-membership metadata such as PlatformEventChannel, PlatformEventChannelMember, or EnrichedField (use integration-eventing-cdc-configure)."
}

integration-connectivity-generate: Salesforce Integration Patterns Expert

Use this skill when the user needs integration architecture and runtime plumbing: Named Credentials, External Credentials, External Services, REST/SOAP callout patterns, Platform Events, CDC, and event-driven integration design.

When This Skill Owns the Task

Use integration-connectivity-generate when the work involves:

  • .namedCredential-meta.xml or External Credential metadata
  • outbound REST/SOAP callouts
  • External Service registration from OpenAPI specs
  • Platform Events, CDC, and event-driven architecture
  • choosing sync vs async integration patterns

Delegate elsewhere when the user is:


Required Context to Gather First

Ask for or infer:

  • integration style: outbound callout, inbound event, External Service, CDC, platform event
  • auth method
  • sync vs async requirement
  • system endpoint / spec details
  • rate limits, retry expectations, and failure tolerance
  • whether this is net-new design or repair of an existing integration

Recommended Workflow

1. Choose the integration pattern

Need Default pattern
authenticated outbound API call Named Credential / External Credential + Apex or Flow
spec-driven API client External Service
trigger-originated callout async callout pattern
decoupled event publishing Platform Events
change-stream consumption CDC

2. Choose the auth model

Prefer secure runtime-managed auth:

  • Named Credentials / External Credentials
  • OAuth or JWT via the right credential model
  • no hardcoded secrets in code

3. Generate from the right templates

Use the provided assets under:

  • assets/named-credentials/
  • assets/external-credentials/
  • assets/external-services/
  • assets/callouts/
  • assets/platform-events/
  • assets/cdc/
  • assets/soap/

4. Validate operational safety

Check:

  • timeout and retry handling
  • async strategy for trigger-originated work
  • logging / observability
  • event retention and subscriber implications

5. Hand off deployment or implementation details

Use:


High-Signal Rules

  • never hardcode credentials
  • do not do synchronous callouts from triggers
  • define timeout behavior explicitly
  • plan retries for transient failures
  • use middleware / event-driven patterns when outbound volume is high
  • prefer External Credentials architecture for new development when supported

Common anti-patterns:

  • sync trigger callouts
  • no retry or dead-letter strategy
  • no request/response logging
  • mixing auth setup responsibilities with runtime integration design

Output Format

When finishing, report in this order:

  1. Integration pattern chosen
  2. Auth model chosen
  3. Files created or updated
  4. Operational safeguards
  5. Deployment / testing next step

Suggested shape:

Integration: <summary>
Pattern: <named credential / external service / event / cdc / callout>
Files: <paths>
Safety: <timeouts, retries, async, logging>
Next step: <deploy, register, test, or implement>

Cross-Skill Integration

Need Delegate to Reason
OAuth app setup integration-connectivity-connected-app-configure consumer key / cert / app config
advanced callout service code platform-apex-generate Apex implementation
declarative HTTP callout / Flow wrapper automation-flow-generate Flow orchestration
deploy integration metadata platform-metadata-deploy validation and rollout
use integration from Agentforce agentforce-generate agent action composition

Reference Map

Start here

Event-driven / platform patterns

CLI / automation / scoring

Asset templates

  • assets/named-credentials/ — Named Credential XML templates (OAuth, JWT, Certificate, Custom auth)
  • assets/external-credentials/ — External Credential XML templates (OAuth, JWT)
  • assets/external-services/ — External Service registration template and operations guide
  • assets/callouts/ — REST sync, Queueable, retry handler, and HTTP response handler Apex templates
  • assets/platform-events/ — Platform Event definition, publisher, and subscriber templates
  • assets/cdc/ — CDC handler and subscriber trigger templates
  • assets/soap/ — SOAP callout service template and wsdl2apex guide
  • assets/endpoint-security/ — Remote Site Setting and CSP Trusted Site XML templates

Automation hooks

  • scripts/suggest_credential_setup.py — auto-suggests credential configuration steps when integration files are detected
  • scripts/validate_integration.py — validates integration patterns before agent responses

Output Expectations

When this skill completes an integration task, it produces:

  1. Credential metadata — one or more files in assets/named-credentials/ or assets/external-credentials/ filled with org-specific values
  2. Callout Apex class — a .cls file using the Named Credential pattern, with async/sync pattern chosen based on context
  3. Event/CDC artifacts — Platform Event .object-meta.xml, subscriber trigger, or CDC config (when event-driven pattern is chosen)
  4. Endpoint security metadata — Remote Site Setting and/or CSP Trusted Site XML files
  5. Scoring report — 120-point score across 6 categories (Security, Error Handling, Bulkification, Architecture, Best Practices, Documentation)
  6. Next step — a deployment or testing instruction for the generated artifacts

Score Guide

Score Meaning
108+ strong production-ready integration design
90–107 good design with some hardening left
72–89 workable but needs architectural review
< 72 unsafe / incomplete for deployment
用于配置Salesforce变更数据捕获(CDC),生成PlatformEventChannelMember和Channel元数据。支持订阅对象、创建自定义频道、设置过滤及富化字段,不涉及事件发布或API配置。
启用CDC 配置Change Data Capture 订阅变更事件 设置变更事件过滤器 添加富化字段 创建自定义事件频道 提及PlatformEventChannel 用户希望下游系统接收Salesforce数据变更
skills/integration-eventing-cdc-configure/SKILL.md
npx skills add forcedotcom/sf-skills --skill integration-eventing-cdc-configure -g -y
SKILL.md
Frontmatter
{
    "name": "integration-eventing-cdc-configure",
    "metadata": {
        "version": "1.0"
    },
    "description": "Use to enable Salesforce Change Data Capture (CDC) on a standard or custom object, configure a custom event channel, set a filter expression, or add enrichment fields. TRIGGER broadly on any of: 'enable CDC', 'enable Change Data Capture', 'turn on CDC', 'subscribe X to change events', 'only emit events for', 'filter change events', 'enrich change events', 'create a custom event channel'; or any mention of CDC, change events, PlatformEventChannel, PlatformEventChannelMember, EnrichedField, ChangeEvents channel, enrichment fields, change event filter; or when the user wants a downstream system to receive Salesforce data changes; or when the user touches .platformEventChannelMember-meta.xml \/ .platformEventChannel-meta.xml files. SKIP when publishing platform events, Pub\/Sub API or REST\/SOAP (use integration-connectivity-generate), or ManagedEventSubscription (out of scope for CDC). Always use this skill for CDC channel-membership metadata."
}

Managing Change Data Capture Enablement

Generate the metadata that subscribes Salesforce objects to Change Data Capture: PlatformEventChannelMember files for the default ChangeEvents channel or a custom channel, and PlatformEventChannel files for new custom channels. Covers enrichment fields, filter expressions, and the canonical naming and value formats that the Metadata API actually accepts (which differ from values that appear in many internal test fixtures and code-search hits).

Scope

  • In scope: Generating PlatformEventChannelMember and PlatformEventChannel metadata for CDC. Subscribing standard objects, custom objects, or both. Configuring enrichment fields. Configuring filter expressions. Defining custom data channels.
  • Out of scope: Publishing custom platform events (PE) — that's a different metadata type (PlatformEvent). Pub/Sub API or external Kafka/Bayeux configuration. Pricing/limits guidance — refer the user to the CDC Developer Guide. Programmatic event-bus subscribers in Apex.

Clarifying Questions

Before generating, confirm with the user if not already clear:

  • Which entity (or entities) need CDC enablement? Standard, custom, or both?
  • Default channel (ChangeEvents) or a custom channel? If custom, what's the channel label?
  • Any enrichment fields needed? (Lookup IDs that the consumer needs even when they didn't change.)
  • Any filter expression needed? (A SOQL-WHERE-clause body that gates which change events emit.)

Required Inputs

Gather or infer before proceeding:

  • Source entity API name(s) — e.g. Account, Lead, Order__c. The skill internally translates this to the ChangeEvent entity name (see Workflow step 2).
  • Channel — either ChangeEvents (default) or the developer name of a custom channel ending in __chn.
  • Enrichment fields (optional) — list of field API names on the source object whose values should be included in every change event.
  • Filter expression (optional) — a predicate over fields on the change event payload (e.g. Status__c != null).

Defaults unless specified:

  • Channel: ChangeEvents (the default CDC channel — no path prefix).
  • Enrichment fields: none.
  • Filter expression: none.

If the user provides a clear, complete request, generate immediately without unnecessary back-and-forth.


Workflow

All steps are sequential. Do not skip or reorder.

Before generating anything, know the only valid CDC metadata types: CDC is expressed entirely through PlatformEventChannelMember (one per subscribed entity) and PlatformEventChannel (only for custom channels). Do NOT use <ChangeDataCapture>, .changeDataCapture-meta.xml, changeDataCapture/ directories, EnableChangeDataCapture, or ManagedEventSubscription — these are not in scope for CDC. If you find yourself writing any of them, stop and use a PlatformEventChannelMember file instead.

  1. Identify the channel — if the user names a custom channel, you'll generate a PlatformEventChannel file (see step 4). Otherwise use the literal value ChangeEvents for the default channel.

  2. Translate source entity to ChangeEvent entity name<selectedEntity> is the ChangeEvent type, NOT the source object:

    Source object <selectedEntity> value
    Account AccountChangeEvent
    Lead LeadChangeEvent
    Contact ContactChangeEvent
    Order__c (custom) Order__ChangeEvent
    MyThing__c (custom) MyThing__ChangeEvent

    For standard objects: append ChangeEvent. For custom objects: replace the trailing __c with __ChangeEvent (the double-underscore is preserved).

  3. Generate the channel-member file — one file per (entity, channel) pair. The filename and fullName always use a SINGLE underscore between the entity stem and ChangeEvent — this is independent of how selectedEntity is formatted in the XML body. For custom objects, drop the __c from the source name when forming the filename:

    Source object Filename (and fullName) <selectedEntity> (in XML)
    Account Account_ChangeEvent.platformEventChannelMember-meta.xml AccountChangeEvent
    Lead Lead_ChangeEvent.platformEventChannelMember-meta.xml LeadChangeEvent
    Order__c Order_ChangeEvent.platformEventChannelMember-meta.xml (NOT Order__ChangeEvent) Order__ChangeEvent
    MyThing__c MyThing_ChangeEvent.platformEventChannelMember-meta.xml (NOT MyThing__ChangeEvent) MyThing__ChangeEvent

    The custom-object case is the easiest place to slip — the filename uses single underscore, the selectedEntity keeps its double underscore. Read assets/PlatformEventChannelMember-template.xml as the structural template.

  4. For a custom channel, generate a PlatformEventChannel file — required if any member references a non-default channel. Derive a DeveloperName from the user's label: strip spaces and non-alphanumeric characters, convert to CamelCase, then always append the literal suffix __chn. The filename and the channel's <eventChannel> reference must use this exact form, otherwise the deploy fails with Invalid channel name:

    User says DeveloperName Filename
    Partner Sync PartnerSync__chn PartnerSync__chn.platformEventChannel-meta.xml (NOT Partner_Sync... or PartnerSync...)
    Order Updates OrderUpdates__chn OrderUpdates__chn.platformEventChannel-meta.xml
    data sync DataSync__chn DataSync__chn.platformEventChannel-meta.xml

    Members on this channel reference it by the same DeveloperName: <eventChannel>PartnerSync__chn</eventChannel>. Read assets/PlatformEventChannel-template.xml.

  5. Add enrichment fields if requested — repeat the <enrichedFields><name>FIELD_API_NAME</name></enrichedFields> block for each field. The name must be a single-hop API name on the source entity — verified working with: standard lookup IDs (OwnerId, ParentId), custom lookup fields (MyLookup__c), and custom non-relationship fields (Region__c, Status__c). Relationship traversals like Owner.Name or Parent.Account.Industry are rejected by deploy with "The selected field, X.Y, isn't valid".

  6. Add a filter expression if requested — wrap the predicate in <filterExpression>...</filterExpression>. The body is a WHERE-clause body without the WHERE keyword (e.g. Status__c != null, not WHERE Status__c != null). For supported operators, field types, and pitfalls, read references/filter-expressions.md.


Rules / Constraints

Constraint Rationale
<selectedEntity> is the ChangeEvent type name, not the source object name The Metadata API binds the member to a ChangeEvent entity — passing Account directly fails with "invalid event in selectedEntity".
Member fullName uses single underscore: Account_ChangeEvent The double-underscore form (Account__ChangeEvent) is parsed as <namespace>__<name> and rejected: "Cannot create a new component with the namespace: Account".
Default channel value is exactly ChangeEvents — no path prefix Older fixtures and some docs show data/ChangeEvents; the deploy returns "Unable to find the specified channel" for that value.
Enrichment field names are single-hop API names on the source entity Standard (OwnerId), custom lookup (MyLookup__c), and custom non-relationship (Region__c) all validate. Traversals like Owner.Name are rejected: "The selected field, X.Y, isn't valid".
<filterExpression> body has no WHERE keyword Deploy returns "filter expression has syntax errors: unexpected token: 'WHERE'".
Filter cannot reference IsDeleted or do relationship traversal (Owner.Username) Deploy rejects with "field is invalid".
DateTime fields support only equality in filters (=, !=) — not < / > Deploy returns "Only equality operators are supported for this field type or value". Use a named date literal: LastModifiedDate = TODAY.
Filter RHS must be a literal — no field-to-field comparison BillingCity = ShippingCity returns "unexpected token: 'ShippingCity'".
Compound fields (e.g. BillingAddress) require dotted component access in filter BillingAddress.City = 'X' deploys; flat BillingCity is rejected as "field is invalid"; raw BillingAddress is rejected as "has to be used with a component field". Note this is the OPPOSITE of <enrichedFields>, which uses flat names.
Custom channel filename ends with __chn before the meta-xml suffix Salesforce's MDAPI naming convention; mismatch causes deploy ambiguity.
Custom channel XML must include <channelType>data</channelType> Without data, the channel is rejected for CDC (other types exist for streaming/event channels).
Source custom objects must already exist (or be deployed in the same transaction) The ChangeEvent entity for Foo__c doesn't exist until Foo__c does; member deploy fails otherwise.
Never generate a PlatformEventChannel file for the default ChangeEvents channel The default channel is system-provided. Reference it via <eventChannel>ChangeEvents</eventChannel> on members, but only custom (__chn) channels need a channel-meta file.
PlatformEventChannelMember accepts ONLY four elements: <enrichedFields>, <eventChannel>, <filterExpression>, <selectedEntity> Adding <description>, <isActive>, <masterLabel>, or any other element fails XML schema validation: "Element {...} invalid at this location". Stick to the four documented elements.
PlatformEventChannel accepts ONLY two elements: <channelType> and <label> Adding <masterLabel>, <description>, etc. produces "Element {...}masterLabel invalid at this location in type PlatformEventChannel". Use <label>, not <masterLabel>.
Generated metadata files only — never run sf project deploy start from this skill This skill produces artifacts; deployment is a separate lifecycle concern.

Gotchas

Issue Resolution
Unable to find the specified channel Set <eventChannel>ChangeEvents</eventChannel> (no data/ prefix).
The PlatformEventChannelMember can't be created because it references an invalid event in the "selectedEntity" field Use the ChangeEvent name, not the source object: AccountChangeEvent, not Account.
Cannot create a new component with the namespace: <Object> Rename the file to use a single underscore: Account_ChangeEvent..., not Account__ChangeEvent....
The selected field, X.Y, isn't valid (in <enrichedFields>) Replace Owner.Name with OwnerId. CDC enriches the lookup automatically; only single-hop field API names validate.
filter expression has syntax errors: unexpected token: 'WHERE' Remove the WHERE keyword. The body is the predicate only.
The BillingCity field in the filter expression is invalid (or any flat Address component) Use the compound dotted form: BillingAddress.City, not BillingCity. See references/filter-expressions.md for the full compound-field matrix.
Custom-object member fails with "ChangeEvent doesn't exist" The source object isn't deployed yet. Ensure the Foo__c object metadata is in the same deploy or already in the org.
DUPLICATE_VALUE on second deploy The member is already subscribed. Either delete first or skip — CDC doesn't support upsert on members directly.
sf infra error (TypeInferenceError, DeployMetadata): Could not infer a metadata type for a .changeDataCapture-meta.xml file That file extension and metadata type don't exist. Replace the changeDataCapture/<Entity>.changeDataCapture-meta.xml file with a platformEventChannelMembers/<Entity>_ChangeEvent.platformEventChannelMember-meta.xml file.
User says "subscribe Order__c" but means standard Order Confirm — OrderChangeEvent (standard) and Order__ChangeEvent (custom) are different entities.

Output Expectations

Deliverables:

  • One force-app/.../platformEventChannelMembers/<Entity>_ChangeEvent.platformEventChannelMember-meta.xml per subscribed entity.
  • One force-app/.../platformEventChannels/<DevName>__chn.platformEventChannel-meta.xml per custom channel (if any).

File structure follows the templates in assets/.

After receiving the generated files, the user can verify them with sf project deploy start --dry-run -d <path> --target-org <alias> before deploying. If a dry-run surfaces an unfamiliar error, references/deploy-troubleshooting.md maps the common deploy errors to their metadata-side fixes.


Cross-Skill Integration

Need Delegate to
Generate the source custom object platform-custom-object-generate skill
Generate custom fields referenced by enrichment or filter platform-custom-field-generate skill
Build a permission set for users who consume change events platform-permission-set-generate skill

Reference File Index

File When to read
assets/PlatformEventChannelMember-template.xml Step 3 — starting structure for a channel member
assets/PlatformEventChannel-template.xml Step 4 — starting structure for a custom channel
references/filter-expressions.md Step 6 — for the supported operators and field-type matrix when writing a filter expression
references/deploy-troubleshooting.md When a user reports a dry-run deploy error and asks for help diagnosing it
用于在Salesforce中创建、读取、更新和删除ManagedEventSubscription元数据,支持平台事件订阅及重播配置。仅生成.xml文件,不涉及通道创建或部署。
用户请求订阅平台事件 需要创建托管事件订阅 配置事件重播或通道订阅者 激活/停用或删除订阅 管理ManagedEventSubscription元数据
skills/integration-eventing-subscription-configure/SKILL.md
npx skills add forcedotcom/sf-skills --skill integration-eventing-subscription-configure -g -y
SKILL.md
Frontmatter
{
    "name": "integration-eventing-subscription-configure",
    "metadata": {
        "version": "1.0"
    },
    "description": "Create, read, update, and delete ManagedEventSubscription metadata in Salesforce. Use this skill for any work involving managed event subscriptions, platform event subscriptions, event channel subscribers, or .managedEventSubscription-meta.xml files. TRIGGER when: user asks to subscribe to a platform event, create a managed subscription, set up event replay, configure an event channel subscriber, update replay preset, activate or deactivate a subscription, delete a subscription, or manage ManagedEventSubscription metadata. SKIP when: user needs to create the platform event channel itself (use platform-custom-object-generate skill) or needs Flow-based event subscriptions (use automation-flow-generate skill)."
}

Managing ManagedEventSubscription

Create, read, update, and delete ManagedEventSubscription metadata — the Salesforce construct for durably subscribing to platform event channels with managed replay tracking.

Scope

  • In scope: Generating and modifying .managedEventSubscription-meta.xml files for create, read, update, and delete operations
  • Out of scope: Creating the underlying platform event (__e) channel itself; Flow-based or Apex-based event subscriptions; deploying metadata to an org
  • Only generate one file — the .managedEventSubscription-meta.xml file. Do NOT generate the referenced platform event object or any other metadata type.

Clarifying Questions

Before generating, confirm if not already clear:

  • What is the topic name? (see format table in references/topic-name-formats.md)
  • What is the developer name? (required for Create — alphanumeric and underscores only, no spaces; optional for Read/Update/Delete if Id is known)
  • What is the label (human-readable name)?
  • What default replay preset — LATEST (default) or EARLIEST?
  • What error recovery replay preset — LATEST (default) or EARLIEST?
  • What should the initial state be — RUN (active) or STOP (inactive)? (default: RUN)

Required Inputs

Gather or infer before proceeding:

  • Operation: create, read, update, or delete
  • DeveloperName: required for Create (becomes the filename); optional for Read/Update/Delete if Id is provided instead
  • Id: Tooling API record Id — can be used to identify the subscription for Read/Update/Delete instead of DeveloperName
  • label: human-readable label (can include spaces)
  • topicName: event channel path — read references/topic-name-formats.md for valid formats (platform events, change events, custom channels)
  • defaultReplay: LATEST or EARLIEST (default: LATEST)
  • errorRecoveryReplay: LATEST or EARLIEST (default: LATEST)
  • state: RUN or STOP (default: RUN) — PAUSE is reserved for internal platform use and will be rejected with INVALID_INPUT
  • version: Metadata API version (default: match org API version, e.g. 67.0)

Workflow

Create

  1. Gather inputs — confirm DeveloperName, label, topicName, defaultReplay, errorRecoveryReplay, state, version. Apply defaults for any omitted fields. If DeveloperName is not provided, ask the user — do not derive it from the label.
  2. Confirm the topic exists — ask the user to confirm the event channel already exists in the org before proceeding. Do NOT generate the platform event object yourself — that is out of scope for this skill. If the user says it doesn't exist yet, stop and direct them to create it first using the platform-custom-object-generate skill, then return here.
  3. Read the template — load assets/managed-event-subscription-template.xml as the starting structure.
  4. Generate the file — produce managedEventSubscriptions/<DeveloperName>.managedEventSubscription-meta.xml filled with user-provided values.
  5. Verify — run the checklist below before presenting output.
  6. Guide the user on subscribing — after deployment, the subscription can be identified for Pub/Sub API ManagedSubscribe RPC calls using either the DeveloperName or the record Id. To retrieve the Id, run: SELECT Id, DeveloperName FROM ManagedEventSubscription WHERE DeveloperName='<DeveloperName>' via the Tooling API.

Read

  1. Identify the subscription — accept either Id or DeveloperName; prefer Id if provided.
  2. Show the file pathmanagedEventSubscriptions/<DeveloperName>.managedEventSubscription-meta.xml (if DeveloperName known).
  3. Retrieve and display — read and present the current XML content.

Update

  1. Identify the subscription — accept either Id or DeveloperName; prefer Id if provided.
  2. Read the existing file — load current content before modifying.
  3. Apply changes — update only the specified fields; preserve all others.
  4. Read references/update-constraints.md for fields that cannot be changed after creation.
  5. Verify — run the checklist below before presenting output.

Delete

  1. Identify the subscription — accept either Id or DeveloperName; confirm with the user before proceeding.
  2. Warn — deleting a ManagedEventSubscription permanently removes replay tracking state.
  3. Produce deletion instructions — explain how to remove the file and deploy the destructive change using destructiveChanges.xml.
  4. Read references/delete-guide.md for the destructive deployment procedure.

Rules / Constraints

Constraint Rationale
<topicName> must use a valid path prefix Platform events use /event/Name__e; change events use /data/Name; see references/topic-name-formats.md for all formats
<defaultReplay> and <errorRecoveryReplay> must be LATEST or EARLIEST These are the only valid enum values; any other value fails metadata validation
<state> must be RUN or STOP PAUSE is reserved for internal platform use — the API rejects it with INVALID_INPUT: You can create a managed event subscription state field only to RUN or STOP
All six required elements must be present topicName, defaultReplay, errorRecoveryReplay, label, state, version are all required; omitting any causes a deploy error
DeveloperName must be unique within the org Duplicate names cause DUPLICATE_DEVELOPER_NAME errors
Do not include <namespacePrefix>, <id>, or <createdDate> Read-only platform fields; including them causes deployment failures in unpackaged orgs

Gotchas

Issue Resolution
The topicName field is invalid on deploy Wrong format or the event doesn't exist in the org — read references/topic-name-formats.md for correct path
Replay state lost after delete + recreate Deleting discards stored replay position; recreating starts from defaultReplay — avoid reusing the same DeveloperName after delete
INVALID_TYPE on SOQL query ManagedEventSubscription is only queryable via Tooling API, not standard SOQL
EARLIEST replay on high-volume channels Can trigger up to 72 hours of backlog replay on activation; always confirm with the user
Metadata not supported in older orgs ManagedEventSubscription requires API v60.0+; check org API version
eventChannel or isActive in generated XML These are wrong field names — use topicName and state (RUN/STOP) instead
PAUSE state in generated XML PAUSE is reserved for internal platform use and will be rejected with INVALID_INPUT — only use RUN or STOP
User unsure how to identify subscription for Pub/Sub API Both DeveloperName and record Id can be used with ManagedSubscribe RPC — retrieve the Id via Tooling API if needed: SELECT Id FROM ManagedEventSubscription WHERE DeveloperName='<name>'
Changes not reflected immediately in Pub/Sub API After create/update/delete, the Pub/Sub API can take up to ~2 minutes to reflect the new config; if ManagedSubscribe returns NOT_FOUND, wait and retry

Verification Checklist

Before presenting any generated XML:

  • Does <topicName> follow a valid path format per references/topic-name-formats.md? (/event/Name__e, /data/NameChangeEvent, /data/ChangeEvents, /event/Name__chn, /data/Name__chn)
  • Is <defaultReplay> exactly LATEST or EARLIEST?
  • Is <errorRecoveryReplay> exactly LATEST or EARLIEST?
  • Is <state> exactly RUN or STOP? (PAUSE is invalid for user-created subscriptions)
  • Is <label> populated?
  • Is <version> present (e.g. 67.0)?
  • Are read-only fields (<id>, <createdDate>, <namespacePrefix>) absent?
  • Does the filename match the DeveloperName exactly?

Output Expectations

  • Create / Update: managedEventSubscriptions/<DeveloperName>.managedEventSubscription-meta.xml — this is the only file to generate
  • Delete: instructions to remove the file and deploy via destructiveChanges.xml
  • Read: display of existing file contents

Cross-Skill Integration

Need Delegate to
Create the platform event channel (__e) being subscribed to platform-custom-object-generate skill
Subscribe via Flow (Process Automation) automation-flow-generate skill
Deploy metadata to org platform-metadata-deploy skill

Reference File Index

File When to read
assets/managed-event-subscription-template.xml Before generating any new subscription — use as starting structure
references/topic-name-formats.md When setting <topicName> — covers platform events, change events, and custom channels
references/update-constraints.md During Update workflow — to check which fields are immutable post-creation
references/delete-guide.md During Delete workflow — for destructive change deployment procedure
Salesforce移动应用构建的入口技能,用于根据SDK类型(Mobile SDK或Agentforce SDK)及平台(iOS/Android)将用户路由至对应子技能。
构建Salesforce iOS或Android原生应用 集成Mobile SDK进行数据同步或离线存储 在移动端嵌入Agentforce智能体或聊天功能 使用Swift或Kotlin扩展Salesforce移动体验
skills/mobile-apps-create/SKILL.md
npx skills add forcedotcom/sf-skills --skill mobile-apps-create -g -y
SKILL.md
Frontmatter
{
    "name": "mobile-apps-create",
    "metadata": {
        "version": "1.0"
    },
    "description": "The entry point for building any Salesforce native mobile app on iOS or Android. TRIGGER when the user says: \"build a Salesforce iOS app\", \"add Salesforce login to my Android app\", \"set up Mobile SDK\", \"add MobileSync \/ SmartStore offline storage\", \"embed an Agentforce agent in my mobile app\", \"add Agentforce chat to iOS\/Android\", or otherwise asks to create, extend, or integrate a Salesforce mobile experience in Swift or Kotlin (MSDK, Agentforce SDK, or both). SKIP when the user is building a non-Salesforce mobile app, using React Native \/ Flutter \/ Ionic without Salesforce integration, asking about generic mobile UI design, or working on a Salesforce-adjacent web\/desktop surface (LWC, Experience Cloud, Mobile Publisher branding-only)."
}

Salesforce Mobile

Route the user to the right SDK-family skill for building Salesforce-connected mobile apps. Do not implement features here; child skills own scenario detection and step-by-step instructions.

Before routing

Disambiguate on two dimensions: SDK family (Mobile SDK vs. Agentforce SDK) and platform (iOS vs. Android). They are not mutually exclusive — an app can use both SDKs.

If the user's intent could plausibly map to either SDK, ask before routing. Guessing wrong wastes the user's time because the child skills are platform- and SDK-specific.

Routing — which SDK family?

User's situation SDK
Authenticating end users to Salesforce, syncing records (MobileSync), storing data offline (SmartStore), biometric login, push notifications, REST integration Mobile SDK
Embedding an Agentforce agent — chat UI, agent conversations, conversational features as the primary surface Agentforce SDK
Both (data-driven app with an embedded agent) Mobile SDK first, then Agentforce SDK layered on top

Tiebreakers when both seem to apply

  • Is the agent the primary surface (chat-first app), or a feature inside an otherwise data-driven app?
    • Primary → Agentforce SDK
    • Feature → Mobile SDK; embed the agent via Agentforce SDK alongside it
  • Are end users authenticating into Salesforce data?
    • Yes → Mobile SDK is required (Agentforce SDK can be added on top).
    • No → Agentforce SDK alone is likely sufficient (it uses guest auth).
  • Asking about offline storage, sync, REST, push, or biometrics? → Mobile SDK.
  • Asking about agent conversations, chat UI, or streaming responses? → Agentforce SDK.

When still unclear, ask the user directly.

Routing — which platform?

Platform Mobile SDK skill Agentforce SDK skill
iOS (Swift) ios-mobile-sdk integrate-agentforce-ios
Android (Kotlin) android-mobile-sdk integrate-agentforce-android

If the user wants both platforms, route to each child skill separately — they are independent.

Combined workflows (Mobile SDK + Agentforce SDK)

When an app needs both:

  1. Route to the Mobile SDK platform skill first to scaffold and authenticate.
  2. Route to the Agentforce SDK platform skill to layer the agent surface.
  3. Treat each child skill's instructions as authoritative for its SDK; do not merge their steps. Each SDK owns its own auth setup, dependency installation order, and initialization sequence — interleaving them produces conflicting config and broken init order.

This sequencing is the only multi-skill logic this skill owns. Everything else lives inside the child skills.

Loading a child skill

Invoke the child skill by name through the harness. If it is not available locally, prompt the user to install it with npx skills add <repo>. If the user confirms (or has pre-authorized installs), run the command and load the child skill — do not make the user go figure out how to continue the workflow. If the user declines, stop and explain that the child skill owns the SDK's setup steps and the workflow cannot continue without it. Each child skill is published from a public repo:

Skill Repo Install command
ios-mobile-sdk forcedotcom/SalesforceMobileSDK-Templatesskills/ios-mobile-sdk/ npx --yes skills add forcedotcom/SalesforceMobileSDK-Templates --skill ios-mobile-sdk --yes
android-mobile-sdk forcedotcom/SalesforceMobileSDK-Templatesskills/android-mobile-sdk/ npx --yes skills add forcedotcom/SalesforceMobileSDK-Templates --skill android-mobile-sdk --yes
integrate-agentforce-ios salesforce/AgentforceMobileSDK-iOSskills/integrate-agentforce-ios/ npx --yes skills add salesforce/AgentforceMobileSDK-iOS --skill integrate-agentforce-ios --yes
integrate-agentforce-android salesforce/AgentforceMobileSDK-Androidskills/integrate-agentforce-android/ npx --yes skills add salesforce/AgentforceMobileSDK-Android --skill integrate-agentforce-android --yes

After install, load the child skill and let it take over. Do not inline the child skill's content — the child skill owns scenario detection, prerequisites, and step-by-step instructions.

Dependencies: <repo>
用于在Salesforce LWC中集成原生移动设备能力,如条码扫描、生物识别、位置、NFC等。适用于涉及移动功能开发或提及mobileCapabilities的场景,排除离线验证及通用组件选择。
用户请求使用LWC调用特定移动设备功能(如扫码、支付、日历等) 提及 lightning/mobileCapabilities、mobile capability、Nimbus 或 device capability
skills/mobile-platform-native-capabilities-integrate/SKILL.md
npx skills add forcedotcom/sf-skills --skill mobile-platform-native-capabilities-integrate -g -y
SKILL.md
Frontmatter
{
    "name": "mobile-platform-native-capabilities-integrate",
    "metadata": {
        "version": "1.0"
    },
    "description": "Build a Salesforce LWC that uses native mobile device capabilities — barcode scanner, biometrics, location, NFC, calendar, contacts, document scanner, geofencing, AR space capture, app review, and payments. Use this skill when the user asks for an LWC that scans a barcode, captures a photo of a document, reads location or geofences, prompts for biometrics, reads\/writes the device calendar or contacts, taps NFC, takes a payment, prompts for an app review, or scans an AR space. Also triggers on \"lightning\/mobileCapabilities\", \"mobile capability\", \"Nimbus\", \"device capability\". Do not use for mobile offline \/ Komaci priming reviews (use `mobile-platform-offline-validate`) or for picking generic Lightning base components (use `design-systems-slds-apply`)."
}

Using Mobile Native Capabilities

The lightning/mobileCapabilities module exposes a set of factory functions that return service objects for native device features (barcode scanning, biometrics, location, etc.). Each service extends a common BaseCapability with an isAvailable() method, so an LWC can degrade gracefully on surfaces where the capability is not present (desktop, mobile web).

This skill routes an agent through (1) picking the right capability, (2) loading the authoritative type definitions, and (3) wiring the service into an LWC with the correct availability gating, error handling, and deprecation-aware API choice.

When to Use This Skill

  • User asks for an LWC that uses a device capability listed in the index below.
  • User mentions lightning/mobileCapabilities, "mobile capability", or "Nimbus" by name.
  • User wants to know which mobile native APIs are available, or which one fits their feature.

Do NOT use this skill for:

  • Mobile-offline review of an LWC (lwc:if, inline GraphQL, Komaci-priming violations) — use mobile-platform-offline-validate.
  • Choosing or styling generic Lightning Base Components / SLDS blueprints — use design-systems-slds-apply.

Prerequisites

  • Knowledge that the LWC will run inside a supported mobile container (Salesforce Mobile App, Field Service Mobile App). These capabilities are unavailable on desktop and mobile web; gate every call behind isAvailable().
  • Familiarity with the lightning/mobileCapabilities module declaration (see mobile-capabilities).

Capability Index

Capability Reference One-line use
App Review App Review Prompt the user for a native in-app review.
AR Space Capture AR Space Capture Capture a 3D scan of a physical space using AR.
Barcode Scanner Barcode Scanner Read QR / UPC / EAN / Code-128 / etc. from the camera.
Biometrics Biometrics Authenticate via Face ID / fingerprint.
Calendar Calendar Read or create events on the device calendar.
Contacts Contacts Read or create entries in the device address book.
Document Scanner Document Scanner Scan paper documents using the camera with edge detection.
Geofencing Geofencing Trigger logic when the device crosses a geographic boundary.
Location Location Read GPS coordinates and watch for updates.
NFC NFC Read or write NFC tags.
Payments Payments Take an Apple Pay / Google Pay payment.

Workflow

Step 1 — Identify the capability

Map the user's feature ask to one row of the capability index. If the ask spans multiple capabilities (e.g. "scan a barcode and store it on a contact"), plan for each capability separately — there is one factory function per capability.

Step 2 — Load the shared and capability-specific references

Read these two shared references once per session — they apply to every capability and are not duplicated in the per-capability files:

  • BaseCapability — the common interface with isAvailable() that every service extends.
  • mobile-capabilities — the lightning/mobileCapabilities module declaration showing every re-exported service.

Then open the capability's reference file from the table above. Each per-capability reference contains the service-specific TypeScript API (factory function, service interface, options types, result types, error types) and assumes the two shared references above are already in context.

Do not infer the API from memory — read it. The services evolve and some methods are explicitly @deprecated in favor of newer alternatives.

Step 3 — Wire the service into the LWC

For each capability:

  1. Import the factory from lightning/mobileCapabilities:
    import { getBarcodeScanner } from 'lightning/mobileCapabilities';
    
  2. Get an instance: const scanner = getBarcodeScanner();
  3. Gate the call behind isAvailable():
    if (!scanner.isAvailable()) {
      // graceful fallback or user message
      return;
    }
    
  4. Call the non-deprecated entry point. Several services keep older methods marked @deprecated alongside the recommended one — always prefer the recommended method in the reference.
  5. Wrap the promise in try/catch and handle the typed failure codes the service exposes (e.g. BarcodeScannerFailureCode, LocationServiceFailureCode). User-cancelled vs. permission-denied vs. service-unavailable are distinct UX states.

Step 4 — Surface failure modes to the user

Each service defines its own failure-code enum. Translate codes into user-actionable messages: a USER_DENIED_PERMISSION should ask the user to grant permission; a USER_DISABLED_PERMISSION must direct them to the OS settings; a SERVICE_NOT_ENABLED should be a developer-visible error, not shown to the user.

Step 5 — Stay inside the supported surface

Mobile capabilities are available only when the LWC runs inside a supported Salesforce mobile app. If the same component is rendered on desktop or mobile web, the factory will still return an object but isAvailable() will return false. Never assume availability — gate every call.

Examples

Example — "Scan a barcode and write it into a field"

  1. Map to: Barcode Scanner.
  2. Read Barcode Scanner.
  3. Use scan(options) (not the deprecated beginCapture / resumeCapture / endCapture triple).
  4. In options, set the barcodeTypes to the symbologies needed (default is all supported types) and enableMultiScan: false for a single read.
  5. On resolve, write result[0].value to the bound field. On reject, inspect error.code against BarcodeScannerFailureCode.

Example — "Take an Apple Pay payment for an order total"

  1. Map to: Payments.
  2. Read Payments.
  3. Gate on isAvailable().
  4. Build the payment request object per the reference.
  5. On resolve, surface the transaction id to the calling flow. On reject, handle user-cancelled and payment-failed paths separately.

Verification Checklist

  • Every capability call is preceded by isAvailable().
  • The non-deprecated entry point is used (no beginCapture / resumeCapture / endCapture for barcode, etc.).
  • Each rejection path is mapped to the typed failure code enum.
  • Imports come from lightning/mobileCapabilities, not from a private path.
  • No assumption that the capability runs on desktop or mobile web.

Troubleshooting

  • isAvailable() returns false on a real device — the device is running an unsupported app surface (not Salesforce Mobile or Field Service Mobile), or the service is gated by an org-level setting. The fix is org configuration, not code.
  • TypeScript can't find the import — confirm the LWC has access to lightning/mobileCapabilities. The module is declared globally inside Salesforce mobile containers; outside that, the types must be installed separately.
  • Deprecated barcode methods still work — yes, but new code must use scan() and dismiss(). Refactor any sample code the agent received before returning it.
  • Multiple capabilities in one component — get separate instances per capability (they are independent service objects); do not try to share state between them.
用于审查 Lightning Web Component 的移动端离线兼容性,通过 Komaci 静态分析检查内联 GraphQL、lwc:if 指令及 ESLint 规则违规,提供修复方案以支持 Salesforce Mobile App Plus 离线模式。
mobile offline review Komaci check offline priming audit offline data graph error
skills/mobile-platform-offline-validate/SKILL.md
npx skills add forcedotcom/sf-skills --skill mobile-platform-offline-validate -g -y
SKILL.md
Frontmatter
{
    "name": "mobile-platform-offline-validate",
    "metadata": {
        "version": "1.0"
    },
    "description": "Review a Lightning Web Component for **mobile offline** compatibility — the Komaci offline static analyzer that pre-primes the data graph for Salesforce Mobile App Plus and Field Service Mobile App. Produces a finding list with code-level fixes covering inline GraphQL queries in `@wire` configurations, modern `lwc:if` \/ `lwc:elseif` \/ `lwc:else` directives, and Komaci ESLint rule violations (private wire properties, non-local reactive references, getter side-effects). Use when the user asks for a \"mobile offline review\", \"Komaci check\", \"offline priming audit\", \"offline priming failure\", or \"offline data graph error\", or to validate an LWC against the `@salesforce\/eslint-plugin-lwc-graph-analyzer` recommended ruleset. Do not use for generic LWC code review (use an appropriate domain review skill) or for building LWCs with native mobile capabilities (use `mobile-platform-native-capabilities-integrate`)."
}

Reviewing LWC Mobile Offline

Run a structured offline-priming compliance pass over a Lightning Web Component, producing a report of issues found and code-level fixes to bring the component into compliance with Komaci's static analysis requirements for the Salesforce Mobile App Plus and Field Service Mobile App.

When to Use

  • The user asks for a "mobile offline review", "Komaci check", or "offline priming audit" on a specific LWC.
  • Preparing a component to ship in Salesforce Mobile App Plus or Field Service Mobile App offline mode.
  • Investigating priming failures reported by the offline analyzer.

Do NOT use this skill for:

  • Building an LWC that uses native mobile capabilities (barcode scanner, biometrics, location, etc.) — use mobile-platform-native-capabilities-integrate.
  • Generic LWC code review — use the appropriate domain skill (reviewing-lws-security, reviewing-lwc-rtl, accessibility-code-review).

Prerequisites

  • Component path (LWC bundle under modules/…).
  • Access to the component's JS/TS and HTML templates.
  • Local Node + npm; ability to run npx eslint with the @salesforce/eslint-plugin-lwc-graph-analyzer plugin.

Knowledge Base

Mobile Offline Grounding explains the three violation categories and why each blocks offline priming. Read it before judging. The per-reviewer references below are the source of truth for the rules and remediations:

Workflow

Step 1 — Scope the review

Identify the component bundle: .html, .js/.ts. CSS and meta files are not in scope for offline priming. If the bundle has multiple HTML templates, all are reviewed.

Step 2 — Read the grounding and per-reviewer references

Read Mobile Offline Grounding and the three per-reviewer references end-to-end before judging. Cite the specific reviewer when emitting each finding so the report is auditable.

Step 3 — lwc:if / lwc:elseif / lwc:else (HTML)

Walk every .html file in the bundle and apply the rules in lwc:if Reviewer. For each occurrence of lwc:if={…}, lwc:elseif={…}, or lwc:else, emit a finding with the exact if:true / if:false rewrite — including the nesting required to preserve lwc:elseif and lwc:else semantics.

Step 4 — Inline GraphQL in @wire (JS)

Walk every .js/.ts file in the bundle and apply the rules in Inline GraphQL Reviewer. For each @wire that references a gql template literal directly (or via a top-level constant), emit a finding that names a concrete getter and shows the rewritten @wire configuration.

Step 5 — Komaci ESLint pass (JS)

Run the Komaci ESLint analyzer over the bundle's JS file using the bundled script. It applies the @salesforce/eslint-plugin-lwc-graph-analyzer recommended ruleset with the bundleAnalyzer processor enabled.

scripts/run-komaci.sh path/to/component.js

The script requires @salesforce/eslint-plugin-lwc-graph-analyzer to be resolvable from the working directory, and the component's sibling HTML templates must live next to the JS file (the plugin's bundleAnalyzer processor uses them to resolve the offline data graph). Output is ESLint --format json on stdout.

For each messages[*] entry in the output, group by ruleId and look up the per-rule remediation in Komaci ESLint Reviewer. Emit a finding per (rule, line) pair with the exact remediation text from the reference; do not invent new advice. See the reference for the manual npx eslint ... invocation if the script is unavailable in the runtime environment.

Step 6 — Produce the report

Emit a report in this shape:

## Mobile Offline (Komaci priming)
- <reviewer> — <file>:<startLine>:<startColumn>-<endLine>:<endColumn> — <type>
  Description: <verbatim from the reviewer reference>
  Intent analysis: <verbatim from the reviewer reference>
  Suggested action: <verbatim from the reviewer reference>
  Code: |
    <source snippet from startLine through endLine, optional but
     recommended when the violation spans multiple lines>
  Applied: yes/no

## Summary
- <n> issues found; <m> fixed; <k> deferred (with reason)

For Komaci ESLint findings, take startLine/startColumn/endLine/ endColumn from the ESLint message's line/column/endLine/endColumn. For Inline GraphQL and lwc:if findings, supply the line/column range you observed in the source. If endLine/endColumn are not available for a finding, fall back to <file>:<startLine> and omit the trailing range.

Cite the reviewer (Inline GraphQL / lwc:if / Komaci ESLint rule id) on every finding.

Step 7 — Apply fixes

Apply the remediations directly when the user asked for fixes. If a remediation conflicts with the component's behavior outside offline (e.g. the developer relies on lwc:elseif for readability and the user is not yet shipping to mobile offline), surface the conflict in the deferred list rather than silently rewriting.

Verification Checklist

  • Every lwc:if / lwc:elseif / lwc:else flagged or absent.
  • Every @wire referencing gql checked; inline queries extracted to a getter.
  • Komaci ESLint analyzer was actually run; findings cite real rule ids, not invented ones.
  • Each finding cites the originating reviewer or rule id.
  • No remediation outside the three categories above (other concerns belong to other skills).

Troubleshooting

  • npx eslint cannot find the plugin — install @salesforce/eslint-plugin-lwc-graph-analyzer in the workspace, or use a pinned local install path. The plugin is the canonical source of Komaci rules.
  • bundleAnalyzer related errors — the recommended config drives the bundle processor; do not strip it. The processor expects sibling HTML files to be discoverable. If running on a stripped-down JS file, supply the matching HTML in the temp directory.
  • No findings for a component you expect to fail — confirm the recommended ruleset is applied (not just bundleAnalyzer with empty rules). Some rules require the HTML to be present alongside the JS.
  • Findings duplicate lwc:if from the dedicated reviewer — the Komaci plugin does not check templates; the lwc:if check is HTML-only and comes from Step 3. Findings from Step 5 are JS-only.
用于生成、审查及迁移 Salesforce Industries Common Core 的 System.Callable Apex 类。支持从 VlocityOpenInterface 迁移,确保与 OmniStudio 组件兼容,并执行 120 分评分验证。
创建或审查 System.Callable 类 迁移 VlocityOpenInterface 或 VlocityOpenInterface2 构建供 OmniStudio、Integration Procedures 或 DataRaptors 使用的 Industries callable 扩展
skills/omnistudio-callable-apex-generate/SKILL.md
npx skills add forcedotcom/sf-skills --skill omnistudio-callable-apex-generate -g -y
SKILL.md
Frontmatter
{
    "name": "omnistudio-callable-apex-generate",
    "metadata": {
        "version": "1.0"
    },
    "description": "Salesforce Industries Common Core (OmniStudio\/Vlocity) Apex callable generation and review skill with 120-point scoring. Use when creating, reviewing, or migrating Industries callable Apex implementations. TRIGGER when: user creates or reviews System.Callable classes, migrates VlocityOpenInterface or VlocityOpenInterface2, or builds Industries callable extensions used by OmniStudio, Integration Procedures, or DataRaptors. DO NOT TRIGGER when: generic Apex classes or triggers (use platform-apex-generate), building Integration Procedures (use omnistudio-integration-procedure-generate), authoring OmniScripts (use omnistudio-omniscript-generate), configuring Data Mappers (use omnistudio-datamapper-generate), or analyzing namespace\/dependency issues (use omnistudio-dependencies-analyze)."
}

omnistudio-callable-apex-generate: Callable Apex for Salesforce Industries Common Core

Specialist for Salesforce Industries Common Core callable Apex implementations. Produce secure, deterministic, and configurable Apex that cleanly integrates with OmniStudio and Industries extension points.

Scope

  • In scope: Creating System.Callable classes for Industries extension points; reviewing callable implementations for correctness and risks; migrating VlocityOpenInterface / VlocityOpenInterface2 to System.Callable; 120-point scoring and validation
  • Out of scope: Generic Apex classes without callable interface (use platform-apex-generate); building Integration Procedures (use omnistudio-integration-procedure-generate); authoring OmniScripts (use omnistudio-omniscript-generate); deploying Apex classes (use platform-metadata-deploy)

Core Responsibilities

  1. Callable Generation: Build System.Callable classes with safe action dispatch
  2. Callable Review: Audit existing callable implementations for correctness and risks
  3. Validation & Scoring: Evaluate against the 120-point rubric
  4. Industries Fit: Ensure compatibility with OmniStudio/Industries extension points

Workflow (4-Phase Pattern)

Phase 1: Requirements Gathering

Ask for:

  • Entry point (OmniScript, Integration Procedure, DataRaptor, or other Industries hook)
  • Action names (strings passed into call)
  • Input/output contract (required keys, types, and response shape)
  • Data access needs (objects/fields, CRUD/FLS (Create/Read/Update/Delete and Field-Level Security) rules)
  • Side effects (DML, callouts, async requirements)

Then:

  1. Scan for existing callable classes: Glob: **/*Callable*.cls
  2. Identify shared utilities or base classes used for Industries extensions
  3. Create a task list

Phase 2: Design & Contract Definition

Define the callable contract:

  • Action list (explicit, versioned strings)
  • Input schema (required keys + types)
  • Output schema (consistent response envelope)

Recommended response envelope:

{
  "success": true|false,
  "data": {...},
  "errors": [ { "code": "...", "message": "..." } ]
}

Action dispatch rules:

  • Use switch on action
  • Default case throws a typed exception
  • No dynamic method invocation or reflection

VlocityOpenInterface / VlocityOpenInterface2 contract mapping:

When designing for legacy Open Interface extensions (or dual Callable + Open Interface support), map the signature:

invokeMethod(String methodName, Map<String, Object> inputMap, Map<String, Object> outputMap, Map<String, Object> options)
Parameter Role Callable equivalent
methodName Action selector (same semantics as action) action in call(action, args)
inputMap Primary input data (required keys, types) args.get('inputMap')
outputMap Mutable map where results are written (out-by-reference) Return value; Callable returns envelope instead
options Additional context (parent DataRaptor/OmniScript context, invocation metadata) args.get('options')

Design rules for Open Interface contracts:

  • Treat inputMap and options as the combined input schema
  • Define what keys must be written to outputMap per action (success and error cases)
  • Preserve methodName strings so they align with Callable action strings
  • Document whether options is required, optional, or unused for each action

Phase 3: Implementation Pattern

Vanilla System.Callable (flat args, no Open Interface coupling):

Read assets/pattern_callable_vanilla.cls before generating — use when callers pass flat args and no VlocityOpenInterface integration is required.

Callable skeleton (same inputs as VlocityOpenInterface):

Read assets/pattern_callable_openinterface.cls before generating — use inputMap and options keys in args when integrating with Open Interface or when callers pass that structure.

Input format: Callers pass args as { 'inputMap' => Map<String, Object>, 'options' => Map<String, Object> }. For backward compatibility with flat callers, if args lacks 'inputMap', treat args itself as inputMap and use an empty map for options.

Implementation rules:

  1. Keep call() thin; delegate to private methods or service classes
  2. Validate and coerce input types early (null-safe)
  3. Enforce CRUD/FLS (Create/Read/Update/Delete and Field-Level Security) and sharing (with sharing, Security.stripInaccessible())
  4. Bulkify when args include record collections
  5. Use WITH USER_MODE for SOQL when appropriate
  6. Namespace handling: System.Callable is a standard interface (no namespace prefix required); omnistudio.VlocityOpenInterface2 uses the managed omnistudio package namespace — always qualify it. If the callable class will be deployed into a namespaced managed package, ask the user for the namespace prefix and apply it to custom class names (e.g., myns__Industries_XxxCallable)

VlocityOpenInterface / VlocityOpenInterface2 implementation:

When implementing omnistudio.VlocityOpenInterface or omnistudio.VlocityOpenInterface2, use the signature:

global Boolean invokeMethod(String methodName, Map<String, Object> inputMap,
                           Map<String, Object> outputMap, Map<String, Object> options)

Read assets/pattern_openinterface.cls before generating — complete VlocityOpenInterface2 skeleton with switch on dispatch and outputMap contract.

Open Interface implementation rules:

  • Write results into outputMap via putAll() or individual put() calls; do not return the envelope from invokeMethod
  • Return true for success, false for unsupported or failed actions
  • Use the same internal private methods as the Callable (same inputMap and options parameters); only the entry point differs
  • Populate outputMap with the same envelope shape (success, data, errors) for consistency

Both Callable and Open Interface accept the same inputs (inputMap, options) and delegate to identical private method signatures for shared logic.


Phase 4: Testing & Validation

Minimum tests:

  • Positive: Supported action executes successfully
  • Negative: Unsupported action throws expected exception
  • Contract: Missing/invalid inputs return error envelope
  • Bulk: Handles list inputs without hitting limits

Read assets/pattern_test_class.cls — complete test class skeleton (positive, negative, contract, bulk, and null-args cases) before generating tests.


Migration: VlocityOpenInterface to System.Callable

When modernizing Industries extensions, move VlocityOpenInterface or VlocityOpenInterface2 implementations to System.Callable and keep the action contract stable.

Guidance:

  • Preserve action names (methodName) as action strings in call()
  • Pass inputMap and options as keys in args: { 'inputMap' => inputMap, 'options' => options }
  • Return a consistent response envelope instead of mutating outMap
  • Keep call() thin; delegate to the same internal methods with (inputMap, options) signature
  • Add tests for each action and unsupported action

Read assets/pattern_migration.cls — annotated before/after migration example (VlocityOpenInterface2 → System.Callable) before starting migration work.


Best Practices (120-Point Scoring)

Category Points Key Rules
Contract & Dispatch 20 Explicit action list; switch on; versioned action strings
Input Validation 20 Required keys validated; types coerced safely; null guards
Security 20 with sharing; CRUD/FLS checks; Security.stripInaccessible()
Error Handling 15 Typed exceptions; consistent error envelope; no empty catch
Bulkification & Limits 20 No SOQL/DML in loops; supports list inputs
Testing 15 Positive/negative/contract/bulk tests
Documentation 10 ApexDoc (/** ... */ block comments — Salesforce Apex documentation standard) for class and action methods

Thresholds: ✅ 90+ (Ready) | ⚠️ 70-89 (Review) | ❌ <70 (Block)


Guardrails (Mandatory)

Stop and ask the user if any of these would be introduced:

  • Dynamic method execution based on user input (no reflection)
  • SOQL/DML inside loops
  • without sharing on callable classes
  • Silent failures (empty catch, swallowed exceptions)
  • Inconsistent response shapes across actions

Gotchas

Issue Resolution
Caller passes flat args but code expects inputMap key Guard defensively: if args lacks 'inputMap' key, treat args itself as the input map
call() receives null for args Always null-check args before accessing keys; initialize to empty map if null
Test class uses (Map<String, Object>) svc.call(...) but call returns a wrong type Ensure every action returns the same envelope type (Map<String, Object>) — mixed return types break callers
VlocityOpenInterface2 migration breaks callers that read outputMap by reference After migrating to Callable, callers must read the return value instead of reading outputMap — update all callers
IndustriesCallableException class missing in project This custom exception must be deployed alongside the callable class — include it in every deployment package
Org has both legacy Open Interface and new Callable wired to same action Only one entry point should be active at a time; disable the old interface after confirming the callable works

Common Anti-Patterns

  • call() contains business logic instead of delegating
  • Action names are unversioned or not documented
  • Input maps assumed to have keys without checks
  • Mixed response types (sometimes Map, sometimes String)
  • No tests for unsupported actions

Cross-Skill Integration

Skill When to Use Example
platform-apex-generate General Apex work beyond callable implementations "Create trigger for Account"
platform-custom-object-generate / platform-custom-field-generate Verify object/field availability before coding "Describe Product2 fields"
platform-metadata-deploy Validate/deploy callable classes "Deploy to sandbox"

Reference Skill

Use the core Apex standards, testing patterns, and guardrails in:


Bundled Examples

Output Expectations

Deliverables produced by this skill:

  • <ClassName>.cls — Callable class implementing System.Callable with switch on action dispatch
  • <ClassName>Test.cls — Test class with positive, negative, contract, and bulk test methods
  • IndustriesCallableException.cls — Custom exception class (if not already present in the project)

Notes

  • Prefer deterministic, side-effect-aware callable actions
  • Keep action contracts stable; introduce new actions for breaking changes
  • Avoid long-running work in synchronous callables; use async when needed

Reference File Index

File When to read
assets/pattern_callable_vanilla.cls Phase 3 — vanilla System.Callable skeleton (flat args, no Open Interface coupling)
assets/pattern_callable_openinterface.cls Phase 3 — System.Callable skeleton with inputMap/options args (Open Interface-compatible)
assets/pattern_openinterface.cls Phase 3 — VlocityOpenInterface2 skeleton with switch on dispatch and outputMap contract
assets/pattern_test_class.cls Phase 4 — test class skeleton (positive, negative, contract, bulk, and null-args cases)
assets/pattern_migration.cls Migration — annotated before/after migration pattern (VlocityOpenInterface2 → System.Callable)
examples/Test_QuoteByProductCallable/Industries_QuoteByProductCallable.cls Phase 3 — complete callable implementation with WITH USER_MODE SOQL and error envelope
examples/Test_QuoteByProductCallable/Industries_QuoteByProductCallableTest.cls Phase 4 — full test class covering positive, contract, and unsupported-action cases
examples/Test_QuoteByProductCallable/IndustriesCallableException.cls Phase 3 — custom exception pattern for unsupported actions
examples/Test_QuoteByProductCallable/TRANSCRIPT.md Reference — reasoning transcript for the Quote-by-Product callable example
examples/Test_VlocityOpenInterfaceConversion/MyCustomCallable.cls Phase 3 — migration pattern from legacy VlocityOpenInterface
examples/Test_VlocityOpenInterfaceConversion/MyCustomCallableTest.cls Phase 4 — test class for VlocityOpenInterface migration example
examples/Test_VlocityOpenInterfaceConversion/IndustriesCallableException.cls Phase 3 — custom exception class deployed alongside VlocityOpenInterface conversion
examples/Test_VlocityOpenInterfaceConversion/MyCustomVlocityOpenInterface2.cls Phase 3 — the original legacy VlocityOpenInterface2 class before migration
examples/Test_VlocityOpenInterfaceConversion/TRANSCRIPT.md Reference — reasoning transcript for VlocityOpenInterface conversion
examples/Test_VlocityOpenInterface2Conversion/MyCustomCallable.cls Phase 3 — migration pattern from VlocityOpenInterface2
examples/Test_VlocityOpenInterface2Conversion/MyCustomCallableTest.cls Phase 4 — test class for VlocityOpenInterface2 migration example
examples/Test_VlocityOpenInterface2Conversion/IndustriesCallableException.cls Phase 3 — custom exception class deployed alongside VlocityOpenInterface2 conversion
examples/Test_VlocityOpenInterface2Conversion/MyCustomRemoteClass.cls Phase 3 — remote class used by the VlocityOpenInterface2 migration example
examples/Test_VlocityOpenInterface2Conversion/TRANSCRIPT.md Reference — reasoning transcript for VlocityOpenInterface2 conversion
用于创建和验证OmniStudio Data Mapper(含Extract/Transform/Load/Turbo Extract),支持字段映射、查询优化及100分制评分。适用于数据访问层构建,需先分析依赖。排除Integration Procedures或OmniScripts开发场景。
用户创建Data Mappers 配置字段映射 处理OmniDataTransform元数据 询问DataRaptor/Data Mapper模式
skills/omnistudio-datamapper-generate/SKILL.md
npx skills add forcedotcom/sf-skills --skill omnistudio-datamapper-generate -g -y
SKILL.md
Frontmatter
{
    "name": "omnistudio-datamapper-generate",
    "metadata": {
        "version": "1.0"
    },
    "description": "OmniStudio Data Mapper (formerly DataRaptor) creation and validation with 100-point scoring. Use when building Extract, Transform, Load, or Turbo Extract Data Mappers, mapping Salesforce object fields, or reviewing existing Data Mapper configurations. TRIGGER when: user creates Data Mappers, configures field mappings, works with OmniDataTransform metadata, or asks about DataRaptor\/Data Mapper patterns. DO NOT TRIGGER when: building Integration Procedures (use omnistudio-integration-procedure-generate), authoring OmniScripts (use omnistudio-omniscript-generate), or analyzing cross-component dependencies (use omnistudio-dependencies-analyze)."
}

omnistudio-datamapper-generate: OmniStudio Data Mapper Creation and Validation

Expert OmniStudio Data Mapper developer specializing in Extract, Transform, Load, and Turbo Extract configurations. Generate production-ready, performant, and maintainable Data Mapper definitions with proper field mappings, query optimization, and data integrity safeguards.


Scope

  • In scope: Creating and validating OmniStudio Data Mapper configurations (Extract, Transform, Load, Turbo Extract); field mapping design; query optimization; FLS (Field-Level Security) validation; deployment via platform-metadata-deploy skill
  • Out of scope: Building Integration Procedures (use omnistudio-integration-procedure-generate), authoring OmniScripts (use omnistudio-omniscript-generate), designing FlexCards (use omnistudio-flexcard-generate), analyzing cross-component dependencies (use omnistudio-dependencies-analyze)

Core Responsibilities

  1. Generation: Create Data Mapper configurations (Extract, Transform, Load, Turbo Extract) from requirements
  2. Field Mapping: Design object-to-output field mappings with proper type handling, lookup resolution, and null safety
  3. Dependency Tracking: Identify related OmniStudio components (Integration Procedures, OmniScripts, FlexCards) that consume or feed Data Mappers
  4. Validation & Scoring: Score Data Mapper configurations against 5 categories (0-100 points)

CRITICAL: Orchestration Order

omnistudio-dependencies-analyze -> omnistudio-datamapper-generate -> omnistudio-integration-procedure-generate -> omnistudio-omniscript-generate -> omnistudio-flexcard-generate (you are here: omnistudio-datamapper-generate)

Data Mappers are the data access layer of the OmniStudio stack. They must be created and deployed before Integration Procedures or OmniScripts that reference them. Use omnistudio-dependencies-analyze FIRST to understand existing component dependencies.


Key Insights

Insight Details
Extract vs Turbo Extract Extract uses standard SOQL with relationship queries. Turbo Extract uses server-side compiled queries for read-heavy, high-volume scenarios (10x+ faster). Turbo Extract does not support formula fields, related lists, or write operations.
Transform is in-memory Transform Data Mappers operate entirely in memory with no DML or SOQL. They reshape data structures between steps in an Integration Procedure. Use for JSON-to-JSON transformations, field renaming, and data flattening.
Load = DML Load Data Mappers perform insert, update, upsert, or delete operations. They require proper FLS checks and error handling. Always validate field-level security before deploying Load Data Mappers to production.
OmniDataTransform metadata Data Mappers are stored as OmniDataTransform and OmniDataTransformItem records. Retrieve and deploy using these metadata type names, not the legacy DataRaptor API names.

Workflow (5-Phase Pattern)

Phase 1: Requirements Gathering

Ask the user to gather:

  • Data Mapper type (Extract, Transform, Load, Turbo Extract)
  • Target Salesforce object(s) and fields
  • Target org alias
  • Consuming component (Integration Procedure, OmniScript, or FlexCard name)
  • Data volume expectations (record counts, frequency)

Then:

  1. Check existing Data Mappers: Glob: **/OmniDataTransform*
  2. Check existing OmniStudio metadata: Glob: **/omnistudio/**
  3. Create a task list

Phase 2: Design & Type Selection

Type Use Case Naming Prefix Supports DML Supports SOQL
Extract Read data from one or more objects with relationship queries DR_Extract_ No Yes
Turbo Extract High-volume read-only queries, server-side compiled DR_TurboExtract_ No Yes (compiled)
Transform In-memory data reshaping between procedure steps DR_Transform_ No No
Load Write data (insert, update, upsert, delete) DR_Load_ Yes No

Naming Format: [Prefix][Object]_[Purpose] using PascalCase

Examples:

  • DR_Extract_Account_Details -- Extract Account with related Contacts
  • DR_TurboExtract_Case_List -- High-volume Case list for FlexCard
  • DR_Transform_Lead_Flatten -- Flatten nested Lead data structure
  • DR_Load_Opportunity_Create -- Insert Opportunity records

Phase 3: Generation & Validation

For Generation:

  1. Read assets/omni-data-transform-extract.json (Extract), assets/omni-data-transform-transform.json (Transform), or assets/omni-data-transform-load.json (Load) for the OmniDataTransform record template
  2. Read assets/omni-data-transform-item.json for each field mapping (OmniDataTransformItem) template
  3. Configure query filters, sort order, and limits for Extract types
  4. Set up lookup mappings and default values for Load types
  5. Validate field-level security for all mapped fields

For Review:

  1. Read existing Data Mapper configuration
  2. Run validation against best practices
  3. Generate improvement report with specific fixes

Run Validation: Read assets/completion-summary-template.md for the scoring output format and thresholds.


Generation Guardrails (MANDATORY)

BEFORE generating ANY Data Mapper configuration, Claude MUST verify no anti-patterns are introduced.

If ANY of these patterns would be generated, STOP and ask the user:

"I noticed [pattern]. This will cause [problem]. Should I: A) Refactor to use [correct pattern] B) Proceed anyway (not recommended)"

Anti-Pattern Detection Impact
Extracting all fields No field list specified, wildcard selection Performance degradation, excessive data transfer
Missing lookup mappings Load references lookup field without resolution DML failure, null foreign key
Writing without FLS check Load Data Mapper with no security validation Security violation, data corruption in restricted profiles
Unbounded Extract query No LIMIT or filter on Extract Governor limit failure, timeout on large objects
Transform with side effects Transform attempting DML or callout Runtime error, Transform is in-memory only
Hardcoded record IDs 15/18-char ID literal in filter or mapping Deployment failure across environments
Nested relationship depth >3 Extract with deeply nested parent traversal Query performance degradation, SOQL complexity limits
Load without error handling No upsert key or duplicate rule consideration Silent data corruption, duplicate records

DO NOT generate anti-patterns even if explicitly requested. Ask user to confirm the exception with documented justification.

See: references/best-practices.md for detailed patterns See: references/naming-conventions.md for naming rules


Phase 4: Deployment

Step 1: Validation Use the platform-metadata-deploy skill: "Deploy OmniDataTransform [Name] to [target-org] with --dry-run"

Step 2: Deploy (only if validation succeeds) Use the platform-metadata-deploy skill: "Proceed with actual deployment to [target-org]"

Post-Deploy: Activate the Data Mapper in the target org. Verify it appears in OmniStudio Designer.

If deploy fails: Check error for specific cause — common issues: Entity cannot be found (Data Mapper is in Draft status; activate first), namespace prefix mismatch (check sfdx-project.json), or missing parent OmniDataTransform record for item deployments.

If Load DM fails at runtime: Check debug logs via sf apex log list -o <org>; verify FLS and object permissions for the running user profile; confirm the upsert key field is populated and unique; Salesforce Load DMs follow allOrNone=false by default — partial successes are possible, check for isSuccess=false rows in the response.


Phase 5: Testing & Documentation

Completion Summary: Read assets/completion-summary-template.md for the completion summary format.

Testing Checklist:

  • Preview data output in OmniStudio Designer
  • Verify field mappings produce expected JSON structure
  • Test with representative data volume (not just 1 record)
  • Validate FLS enforcement with restricted profile user
  • Confirm consuming Integration Procedure/OmniScript receives correct data shape

Best Practices (100-Point Scoring)

Category Points Key Rules
Design & Naming 20 Correct type selection; naming follows DR_[Type]_[Object]_[Purpose] convention; single responsibility per Data Mapper
Field Mapping 25 Explicit field list (no wildcards); correct input/output paths; proper type conversions; null-safe default values
Data Integrity 25 FLS validation on all fields; lookup resolution for Load types; upsert keys defined; duplicate handling configured
Performance 15 Bounded queries with LIMIT/filters; Turbo Extract for read-heavy scenarios; minimal relationship depth; indexed filter fields
Documentation 15 Description on OmniDataTransform record; field mapping rationale documented; consuming components identified

Thresholds: ✅ 90+ (Deploy) | ⚠️ 67-89 (Review) | ❌ <67 (Block - fix required)


CLI Commands

Query Existing Data Mappers

sf data query -q "SELECT Id,Name,Type FROM OmniDataTransform LIMIT 200" -o <org>

Query Data Mapper Field Mappings

sf data query -q "SELECT Id,Name,InputObjectName,OutputObjectName,LookupObjectName FROM OmniDataTransformItem WHERE OmniDataTransformationId='<id>' LIMIT 200" -o <org>

Retrieve Data Mapper Metadata

sf project retrieve start -m OmniDataTransform:<Name> -o <org>

Deploy Data Mapper Metadata

sf project deploy start -m OmniDataTransform:<Name> -o <org>

Output Expectations

Deliverables produced by this skill:

  • OmniDataTransform record — main Data Mapper record built from assets/omni-data-transform-*.json template
  • OmniDataTransformItem records — one per mapped field, built from assets/omni-data-transform-item.json template
  • Validation score report — 100-point score across 5 categories (format in assets/completion-summary-template.md)
  • Deployment confirmation — Data Mapper activated and visible in OmniStudio Designer

Cross-Skill Integration

From Skill To omnistudio-datamapper-generate When
omnistudio-dependencies-analyze -> omnistudio-datamapper-generate "Analyze dependencies before creating Data Mapper"
platform-custom-object-generate / platform-custom-field-generate -> omnistudio-datamapper-generate "Describe target object fields before mapping"
platform-soql-query -> omnistudio-datamapper-generate "Validate Extract query logic"
From omnistudio-datamapper-generate To Skill When
omnistudio-datamapper-generate -> omnistudio-integration-procedure-generate "Create Integration Procedure that calls this Data Mapper"
omnistudio-datamapper-generate -> platform-metadata-deploy "Deploy Data Mapper to target org"
omnistudio-datamapper-generate -> omnistudio-omniscript-generate "Wire Data Mapper output into OmniScript"
omnistudio-datamapper-generate -> omnistudio-flexcard-generate "Display Data Mapper Extract results in FlexCard"

Gotchas

Issue Resolution
Large data volume (>10K records) Use Turbo Extract; add pagination via Integration Procedure; warn about heap limits
Polymorphic lookup fields Specify the concrete object type in the mapping; test each type separately
Formula fields in Extract Standard Extract supports formula fields; Turbo Extract does not — fall back to standard Extract
Cross-object Load (master-detail) Insert parent records first, then child records in a separate Load step; use Integration Procedure to orchestrate sequence
Namespace-prefixed fields Include namespace prefix in field paths (e.g., ns__Field__c); verify prefix matches target org
Multi-currency orgs Map CurrencyIsoCode explicitly; do not rely on default currency assumption
RecordType-dependent mappings Filter by RecordType in Extract; set RecordTypeId in Load; document which RecordTypes are supported
Draft Data Mapper not retrievable sf project retrieve start -m OmniDataTransform:<Name> only works for active DMs; activate before retrieving
Foreign key field name wrong The parent lookup on OmniDataTransformItem is OmniDataTransformationId (full word "Transformation"), not OmniDataTransformId

Notes

  • Metadata Type: OmniDataTransform (not DataRaptor — legacy name deprecated)
  • API Version: Requires OmniStudio managed package or Industries Cloud
  • Scoring: Block deployment if score < 67; read assets/completion-summary-template.md for score format
  • Turbo Extract Limitations: No formula fields, no related lists, no aggregate queries, no polymorphic fields
  • Activation: Data Mappers must be activated after deployment to be callable from Integration Procedures (see Gotchas for draft retrieval behavior)
  • Creating via Data API: Use sf api request rest --method POST --body @file.json to create OmniDataTransform and OmniDataTransformItem records. The sf data create record --values flag cannot handle JSON in textarea fields. Write the JSON body to a temp file first.

Reference File Index

File When to Read
assets/omni-data-transform-extract.json Phase 3 Generation — template for Extract type OmniDataTransform records
assets/omni-data-transform-transform.json Phase 3 Generation — template for Transform type OmniDataTransform records
assets/omni-data-transform-load.json Phase 3 Generation — template for Load type OmniDataTransform records
assets/omni-data-transform-item.json Phase 3 Generation — template for each OmniDataTransformItem field mapping
assets/completion-summary-template.md Phase 3 & 5 — scoring output format and completion summary template
references/best-practices.md Phase 3 Guardrails — detailed patterns for field mapping, query optimization, null handling, and performance
references/naming-conventions.md Phase 2 Design — full naming rules for all Data Mapper types and field mapping conventions

用于自动化Salesforce Industries OmniStudio/Vlocity DataPack的部署、导出、重试及CI/CD流程。涵盖通过vlocity命令进行数据迁移、依赖排错及预检验证,区别于标准元数据或代码构建。
使用vlocity命令(如packDeploy/packExport)部署或验证DataPack 配置DataPack CI/CD流水线 排查DataPack迁移中的依赖或匹配键错误
skills/omnistudio-datapacks-deploy/SKILL.md
npx skills add forcedotcom/sf-skills --skill omnistudio-datapacks-deploy -g -y
SKILL.md
Frontmatter
{
    "name": "omnistudio-datapacks-deploy",
    "metadata": {
        "version": "1.0"
    },
    "description": "Salesforce Industries DataPack deployment automation using Vlocity Build. TRIGGER when: user deploys or validates OmniStudio\/Vlocity DataPacks with vlocity commands (packDeploy\/packRetry\/packExport\/packGetDiffs), sets up DataPack CI\/CD pipelines, or troubleshoots DataPack migration errors. DO NOT TRIGGER when: deploying Salesforce metadata with sf project deploy (use platform-metadata-deploy), authoring OmniStudio artifacts (use omnistudio-*-build), or writing Apex\/LWC business logic (use platform-apex-generate\/experience-lwc-generate)."
}

omnistudio-datapacks-deploy: Vlocity Build DataPack Deployment

Use this skill when the user needs Vlocity DataPack deployment orchestration: export/deploy workflow, manifest-driven deploys, failure triage, and CI/CD sequencing for OmniStudio/Industries DataPacks.


Scope

Use omnistudio-datapacks-deploy when work involves:

  • vlocity packDeploy, packRetry, packContinue, packExport, packGetDiffs, validateLocalData
  • DataPack job-file design (projectPath, expansionPath, manifest, queries)
  • org-to-org DataPack migration and retry loops
  • troubleshooting DataPack dependency, matching-key, and GlobalKey issues

Delegate elsewhere when the user is:


Critical Operating Rules

  • Use Vlocity Build (vlocity) commands for DataPacks, not sf project deploy.
  • Prefer Salesforce CLI auth integration (-sfdx.username <alias>) over username/password files when available.
  • Always run a pre-deploy quality gate before full deploy:
    1. validateLocalData
    2. optional packGetDiffs
    3. then packDeploy
  • Use packRetry repeatedly when error counts are dropping; stop when retries no longer improve results.
  • Keep matching-key strategy and GlobalKey integrity consistent across source and target orgs.

Required Context to Gather First

Ask for or infer:

  • source org and target org aliases
  • job file path and DataPack project path
  • deployment scope (full project, manifest subset, or specific -key)
  • whether this is export, deploy, retry, continue, or diff-only
  • namespace model (%vlocity_namespace%, vlocity_cmt, or core)
  • known constraints (new sandbox bootstrap, trigger behavior, matching key customizations)

Preflight checks:

vlocity help
sf org list
sf org display --target-org <alias> --json
test -f <job-file>.yaml

Recommended Workflow

1. Ensure tool readiness

npm install --global vlocity
vlocity help

2. Validate project data locally

vlocity -sfdx.username <source-alias> -job <job-file>.yaml validateLocalData

Use --fixLocalGlobalKeys only when explicitly requested and after explaining impact.

3. Export from source (when needed)

vlocity -sfdx.username <source-alias> -job <job-file>.yaml packExport
vlocity -sfdx.username <source-alias> -job <job-file>.yaml packRetry

4. Deploy to target

vlocity -sfdx.username <target-alias> -job <job-file>.yaml packDeploy
vlocity -sfdx.username <target-alias> -job <job-file>.yaml packRetry

5. Continue interrupted jobs

vlocity -sfdx.username <target-alias> -job <job-file>.yaml packContinue

6. Verify post-deploy parity

vlocity -sfdx.username <target-alias> -job <job-file>.yaml packGetDiffs

Job-file starter: references/job-file-template.md


Gotchas

Error / symptom Likely cause Default fix direction
No match found for ... missing dependency in target org include missing DataPack key and redeploy
Duplicate Results found for ... GlobalKey duplicate records in target clean duplicates and re-run deploy
Multiple Imported Records ... same Salesforce Record source duplicate matching-key records remove duplicates in source and re-export
No Configuration Found outdated DataPack settings run packUpdateSettings or enable autoUpdateSettings
Some records were not processed settings mismatch / partial dependency state refresh settings both orgs, then retry
SASS / template compile failures missing referenced UI template assets export/deploy referenced template dependencies first

Detailed matrix: references/troubleshooting-matrix.md


CI/CD Guidance

Default pipeline shape:

  1. authenticate orgs (sf org login ...)
  2. validate local DataPack integrity (validateLocalData)
  3. export changed scope (packExport or manifest-driven export)
  4. deploy (packDeploy)
  5. retry loop (packRetry) until stable
  6. compare (packGetDiffs) and publish deployment report

For incremental deploy optimization, use job-file options such as:

  • gitCheck: true
  • gitCheckKey: <folder>
  • manifest for deterministic scope control

Cross-Skill Integration

Need Delegate to Reason
metadata deploy outside DataPacks platform-metadata-deploy Metadata API workflows
OmniStudio component authoring omnistudio-*-build build artifacts before deploy
EPC product and offer payload authoring omnistudio-epc-catalog-generate Product2/DataPack model quality
Apex trigger/log error diagnosis platform-apex-logs-debug, platform-apex-generate automation-side root-cause fixes

Output Expectations

After completing a DataPack operation, deliver a completion block:

DataPack goal: <export / deploy / retry / diff / ci-cd>
Source org: <alias or N/A>
Target org: <alias or N/A>
Scope: <job file + manifest/key/full>
Result: <passed / failed / partial>
Key findings: <errors, dependencies, retries, diffs>
Next step: <safe follow-up action>

Reference File Index

File When to read
references/job-file-template.md Before advising on job file structure — load as baseline configuration reference
references/troubleshooting-matrix.md When user reports deploy failures — load to diagnose DataPack errors and apply fix directions
examples/business-internet-plus-bundle/TRANSCRIPT.md Example of validation planning and execution for a Product2 bundle
examples/business-internet-plus-bundle/deploy-business-internet-plus-bundle.yaml Example job file for scope-limited validateLocalData run
examples/business-internet-plus-bundle-deploy/TRANSCRIPT.md Example of full deploy cycle including packDeploy and packRetry outcomes
examples/business-internet-plus-bundle-deploy/deploy-business-internet-plus-bundle.yaml Example job file for staged deployment with manifest targeting
OmniStudio跨组件分析技能,专注命名空间检测、依赖图谱构建及影响分析。适用于查询组件依赖关系、生成Mermaid可视化图表或评估变更影响。不适用于创建或修改具体组件(如OmniScripts、FlexCards等)。
用户询问OmniStudio组件间的依赖关系 需要检测命名空间类型(Core/vlocity_cmt/vlocity_ins) 请求进行变更影响分析 要求生成依赖图或Mermaid可视化图表 想知道哪些组件受特定更改影响
skills/omnistudio-dependencies-analyze/SKILL.md
npx skills add forcedotcom/sf-skills --skill omnistudio-dependencies-analyze -g -y
SKILL.md
Frontmatter
{
    "name": "omnistudio-dependencies-analyze",
    "metadata": {
        "version": "1.0"
    },
    "description": "Cross-cutting OmniStudio analysis skill for namespace detection, dependency visualization, and impact analysis across OmniScripts, FlexCards, Integration Procedures, and Data Mappers. TRIGGER when: user asks about OmniStudio dependencies, wants namespace detection (Core vs vlocity_cmt vs vlocity_ins), needs impact analysis, requests dependency graphs or Mermaid diagrams, or asks which components are affected by a change. DO NOT TRIGGER when: authoring OmniScripts (use omnistudio-omniscript-generate), building FlexCards (use omnistudio-flexcard-generate), creating Integration Procedures (use omnistudio-integration-procedure-generate), or configuring Data Mappers (use omnistudio-datamapper-generate)."
}

omnistudio-dependencies-analyze: OmniStudio Cross-Component Analysis

Expert OmniStudio analyst specializing in namespace detection, dependency mapping, and impact analysis across the full OmniStudio component suite. Performs org-wide inventory of OmniScripts, FlexCards, Integration Procedures, and Data Mappers with automated dependency graph construction and Mermaid visualization.


Scope

  • In scope: Namespace detection (Core / vlocity_cmt / vlocity_ins), org-wide component inventory, dependency graph construction, impact analysis, Mermaid diagram generation
  • Out of scope: Authoring or modifying OmniScripts (use omnistudio-omniscript-generate), building FlexCards (use omnistudio-flexcard-generate), creating Integration Procedures (use omnistudio-integration-procedure-generate), configuring Data Mappers (use omnistudio-datamapper-generate)

Required Inputs

Ask for or infer before starting:

Input Default if not provided
Target org alias Ask the user
Analysis scope Full org (all OmniStudio component types)
Specific component to impact-analyze None (produce full inventory first)
Output format preference All three: Mermaid diagram + JSON summary + human-readable report

Output Expectations

Each analysis run produces one or more of:

  1. Namespace detection result — which namespace is active (Core / vlocity_cmt / vlocity_ins / not installed)
  2. Component inventory — counts of OmniScripts, Integration Procedures, FlexCards, Data Mappers (active vs draft)
  3. Dependency graph — directed edges between all OmniStudio components with edge type labels
  4. Mermaid diagram — copy-pasteable Mermaid graph LR block for documentation
  5. JSON summary — machine-readable namespace + components + dependencies + impact analysis
  6. Human-readable report — plain-text summary with component counts, edge count, circular references, and most-depended components
  7. Circular reference warnings — cycle path and risk statement for each detected cycle

Core Responsibilities

  1. Namespace Detection: Identify whether an org uses Core (Industries), vlocity_cmt (Communications, Media & Energy), or vlocity_ins (Insurance & Health) namespace
  2. Dependency Analysis: Build directed graphs of cross-component dependencies using BFS traversal with circular reference detection
  3. Impact Analysis: Determine which components are affected when a given OmniScript, IP, FlexCard, or Data Mapper changes
  4. Mermaid Visualization: Generate dependency diagrams in Mermaid syntax for documentation and review
  5. Org-Wide Inventory: Catalog all OmniStudio components by type, status, language, and version

CRITICAL: Orchestration Order

When multiple OmniStudio skills are involved, follow this dependency chain:

omnistudio-dependencies-analyzeomnistudio-datamapper-generateomnistudio-integration-procedure-generateomnistudio-omniscript-generateomnistudio-flexcard-generate

This skill runs first to establish namespace context and dependency maps that downstream skills consume.


Key Insights

Insight Detail
Three namespaces coexist Core (OmniProcess), vlocity_cmt (vlocity_cmt__OmniScript__c), vlocity_ins (vlocity_ins__OmniScript__c)
Dependencies are stored in JSON PropertySetConfig (elements), Definition (FlexCards), InputObjectName/OutputObjectName (Data Mappers)
Circular references are possible OmniScript A → IP B → OmniScript A via embedded call
FlexCard data sources are typed dataSource.type === 'IntegrationProcedures' (plural) in DataSourceConfig JSON
Active vs Draft matters Only active components participate in runtime dependency chains

Workflow (4-Phase Pattern)

Phase 1: Namespace Detection

Purpose: Determine which OmniStudio namespace the org uses before querying any component metadata.

Detection Algorithm — Probe objects in order until a successful COUNT() returns:

  1. Core (Industries namespace):

    SELECT COUNT() FROM OmniProcess
    

    If this succeeds, the org uses the Core namespace (API 234.0+ / Spring '22+).

  2. vlocity_cmt (Communications, Media & Energy):

    SELECT COUNT() FROM vlocity_cmt__OmniScript__c
    
  3. vlocity_ins (Insurance & Health):

    SELECT COUNT() FROM vlocity_ins__OmniScript__c
    

If none succeed, OmniStudio is not installed in the org.

CLI Commands for namespace detection:

# Core namespace probe
sf data query --query "SELECT COUNT() FROM OmniProcess" --target-org myorg --json 2>/dev/null

# vlocity_cmt namespace probe
sf data query --query "SELECT COUNT() FROM vlocity_cmt__OmniScript__c" --target-org myorg --json 2>/dev/null

# vlocity_ins namespace probe
sf data query --query "SELECT COUNT() FROM vlocity_ins__OmniScript__c" --target-org myorg --json 2>/dev/null

Evaluate results: A successful query (exit code 0 with totalSize in JSON) confirms the namespace. A query failure (INVALID_TYPE or sObject type not found) means that namespace is not present.

See: references/namespace-guide.md for complete object/field mapping across all three namespaces.


Phase 2: Component Discovery

Purpose: Build an inventory of all OmniStudio components in the org.

Using the detected namespace, query each component type:

OmniScripts (Core example — paginate with LIMIT/OFFSET for large orgs):

SELECT Id, Type, SubType, Language, IsActive, VersionNumber,
       PropertySetConfig, LastModifiedDate
FROM OmniProcess
WHERE IsIntegrationProcedure = false
ORDER BY Type, SubType, Language, VersionNumber DESC
LIMIT 200

Integration Procedures (Core example):

SELECT Id, Type, SubType, Language, IsActive, VersionNumber,
       PropertySetConfig, LastModifiedDate
FROM OmniProcess
WHERE IsIntegrationProcedure = true
ORDER BY Type, SubType, Language, VersionNumber DESC
LIMIT 200

FlexCards (Core example):

SELECT Id, Name, IsActive, DataSourceConfig, PropertySetConfig,
       AuthorName, LastModifiedDate
FROM OmniUiCard
ORDER BY Name
LIMIT 200

IMPORTANT: The OmniUiCard object does NOT have a Definition field. Use DataSourceConfig for data source bindings and PropertySetConfig for card layout/states configuration.

Data Mappers (Core example):

SELECT Id, Name, IsActive, Type, LastModifiedDate
FROM OmniDataTransform
ORDER BY Name
LIMIT 200

Data Mapper Items (for object dependency extraction):

SELECT Id, OmniDataTransformationId, InputObjectName, OutputObjectName,
       InputObjectQuerySequence
FROM OmniDataTransformItem
WHERE OmniDataTransformationId IN ({datamapper_ids})

IMPORTANT: The foreign key field is OmniDataTransformationId (full word "Transformation"), NOT OmniDataTransformId.

CLI Command pattern:

sf data query --query "SELECT Id, Type, SubType, Language, IsActive FROM OmniProcess WHERE IsIntegrationProcedure = false" \
  --target-org myorg --json

Phase 3: Dependency Analysis

Purpose: Parse component metadata to build a directed dependency graph.

Algorithm: BFS with Circular Detection

1. Initialize empty graph G and visited set V
2. For each root component C:
   a. Enqueue C into work queue Q
   b. While Q is not empty:
      i.   Dequeue component X from Q
      ii.  If X is in V, record circular reference and skip
      iii. Add X to V
      iv.  Parse X's metadata for dependency references
      v.   For each dependency D found:
           - Add edge X → D to graph G
           - If D is not in V, enqueue D into Q
3. Return graph G and any circular references detected

Element Type → Dependency Extraction

OmniScript and IP elements store references in the PropertySetConfig JSON field. Parse each element to extract dependencies:

Element Type JSON Path in PropertySetConfig Dependency Target
DataRaptor Transform Action bundle, bundleName Data Mapper (by name)
DataRaptor Turbo Action bundle, bundleName Data Mapper (by name)
Remote Action remoteClass, remoteMethod Apex Class.Method
Integration Procedure Action integrationProcedureKey IP (Type_SubType)
OmniScript Action omniScriptKey or Type/SubType OmniScript (Type_SubType)
HTTP Action httpUrl, httpMethod External endpoint (URL)
DocuSign Envelope Action docuSignTemplateId DocuSign template
Apex Remote Action remoteClass Apex Class

Parsing PropertySetConfig:

For each OmniProcessElement:
  1. Read PropertySetConfig (JSON string)
  2. Parse JSON
  3. Check element.Type against extraction table
  4. Extract referenced component name/key
  5. Resolve reference to an OmniProcess/OmniDataTransform record
  6. Add edge: parent component → referenced component

FlexCard Data Source Parsing

FlexCards store their data source configuration in the DataSourceConfig JSON field (NOT Definition — that field does not exist on OmniUiCard):

Parse DataSourceConfig JSON:
  1. Access dataSource object (singular, not array)
  2. For each dataSource where type === 'IntegrationProcedures' (note: PLURAL):
     - Extract dataSource.value.ipMethod (IP Type_SubType)
     - Add edge: FlexCard → Integration Procedure
  3. For each dataSource where type === 'ApexRemote':
     - Extract dataSource.value.className
     - Add edge: FlexCard → Apex Class
  4. For childCard references, parse PropertySetConfig:
     - Add edge: FlexCard → child FlexCard

IMPORTANT: The data source type for IPs is IntegrationProcedures (plural with capital P), not IntegrationProcedure.

Data Mapper Object Dependencies

Data Mappers reference Salesforce objects via their items:

For each OmniDataTransformItem:
  1. Read InputObjectName → source sObject
  2. Read OutputObjectName → target sObject
  3. Add edge: Data Mapper → sObject (read from InputObjectName)
  4. Add edge: Data Mapper → sObject (write to OutputObjectName)

See: references/dependency-patterns.md for complete dependency extraction rules and examples.


Phase 4: Visualization & Reporting

Purpose: Generate human-readable output from the dependency graph.

Output Format 1: Mermaid Dependency Diagram

graph LR
    subgraph OmniScripts
        OS1["createOrder<br/>English v3"]
        OS2["updateAccount<br/>English v1"]
    end
    subgraph Integration Procedures
        IP1["fetchAccountData<br/>English v2"]
        IP2["submitOrder<br/>English v1"]
    end
    subgraph Data Mappers
        DM1["AccountExtract"]
        DM2["OrderTransform"]
    end
    subgraph FlexCards
        FC1["AccountSummaryCard"]
    end

    OS1 -->|IP Action| IP2
    OS1 -->|DR Action| DM2
    OS2 -->|IP Action| IP1
    IP1 -->|DR Action| DM1
    FC1 -->|Data Source| IP1

    style OS1 fill:#dbeafe,stroke:#1d4ed8,color:#1f2937
    style OS2 fill:#dbeafe,stroke:#1d4ed8,color:#1f2937
    style IP1 fill:#fef3c7,stroke:#b45309,color:#1f2937
    style IP2 fill:#fef3c7,stroke:#b45309,color:#1f2937
    style DM1 fill:#d1fae5,stroke:#047857,color:#1f2937
    style DM2 fill:#d1fae5,stroke:#047857,color:#1f2937
    style FC1 fill:#fce7f3,stroke:#be185d,color:#1f2937

Color scheme:

Component Type Fill Stroke
OmniScript #dbeafe (blue-100) #1d4ed8 (blue-700)
Integration Procedure #fef3c7 (amber-100) #b45309 (amber-700)
Data Mapper #d1fae5 (green-100) #047857 (green-700)
FlexCard #fce7f3 (pink-100) #be185d (pink-700)
Apex Class #e9d5ff (purple-100) #7c3aed (purple-700)
External (HTTP) #f1f5f9 (slate-100) #475569 (slate-600)

Output Format 2: JSON Summary

{
  "namespace": "Core",
  "components": {
    "omniScripts": 12,
    "integrationProcedures": 8,
    "flexCards": 5,
    "dataMappers": 15
  },
  "dependencies": [
    { "from": "OS:createOrder", "to": "IP:submitOrder", "type": "IPAction" },
    { "from": "IP:fetchAccountData", "to": "DM:AccountExtract", "type": "DataRaptorAction" }
  ],
  "circularReferences": [],
  "impactAnalysis": {
    "DM:AccountExtract": {
      "directDependents": ["IP:fetchAccountData"],
      "transitiveDependents": ["OS:updateAccount", "FC:AccountSummaryCard"]
    }
  }
}

Output Format 3: Human-Readable Report

OmniStudio Dependency Report
=============================
Org Namespace: Core (Industries)
Scan Date: 2026-03-06

Component Inventory:
  OmniScripts:              12 (8 active, 4 draft)
  Integration Procedures:    8 (6 active, 2 draft)
  FlexCards:                  5 (5 active)
  Data Mappers:             15 (12 active, 3 draft)

Dependency Summary:
  Total edges:              23
  Circular references:       0
  Orphaned components:       2 (no inbound/outbound deps)

Impact Analysis (most-depended components):
  1. DM:AccountExtract       → 5 dependents
  2. IP:fetchAccountData     → 3 dependents
  3. DM:OrderTransform       → 2 dependents

Namespace Object/Field Mapping

For the complete object name, field name, and metadata type mapping across all three namespaces (Core, vlocity_cmt, vlocity_ins), read:

references/namespace-guide.md

Key discriminators to keep in mind:

  • Core uses OmniProcess / OmniUiCard / OmniDataTransform
  • vlocity_cmt uses vlocity_cmt__OmniScript__c / vlocity_cmt__VlocityUITemplate__c / vlocity_cmt__DRBundle__c
  • vlocity_ins uses vlocity_ins__OmniScript__c / vlocity_ins__VlocityUITemplate__c / vlocity_ins__DRBundle__c
  • The IsIntegrationProcedure boolean and DataSourceConfig (not Definition) field names are Core-only

CLI Commands Reference

Namespace Detection

# Probe all three namespaces (run sequentially, first success wins)
sf data query --query "SELECT COUNT() FROM OmniProcess" --target-org myorg --json 2>/dev/null && echo "CORE" || \
sf data query --query "SELECT COUNT() FROM vlocity_cmt__OmniScript__c" --target-org myorg --json 2>/dev/null && echo "VLOCITY_CMT" || \
sf data query --query "SELECT COUNT() FROM vlocity_ins__OmniScript__c" --target-org myorg --json 2>/dev/null && echo "VLOCITY_INS" || \
echo "NOT_INSTALLED"

Component Inventory (Core Namespace)

# Count OmniScripts
sf data query --query "SELECT COUNT() FROM OmniProcess WHERE IsIntegrationProcedure = false" \
  --target-org myorg --json

# Count Integration Procedures
sf data query --query "SELECT COUNT() FROM OmniProcess WHERE IsIntegrationProcedure = true" \
  --target-org myorg --json

# Count FlexCards
sf data query --query "SELECT COUNT() FROM OmniUiCard" --target-org myorg --json

# Count Data Mappers
sf data query --query "SELECT COUNT() FROM OmniDataTransform" --target-org myorg --json

Dependency Data Extraction (Core Namespace)

# Get OmniScript elements with their config
sf data query --query "SELECT Id, OmniProcessId, Name, Type, PropertySetConfig FROM OmniProcessElement WHERE OmniProcessId = '{process_id}'" \
  --target-org myorg --json

# Get FlexCard data sources (for dependency parsing)
sf data query --query "SELECT Id, Name, DataSourceConfig FROM OmniUiCard WHERE IsActive = true" \
  --target-org myorg --json

# Get Data Mapper items (for object dependencies)
sf data query --query "SELECT Id, OmniDataTransformationId, InputObjectName, OutputObjectName FROM OmniDataTransformItem" \
  --target-org myorg --json

Cross-Skill Integration

Skill Relationship How This Skill Helps
omnistudio-datamapper-generate Provides namespace and object dependency data Data Mapper authoring uses detected namespace for correct API names
omnistudio-integration-procedure-generate Provides namespace and IP dependency map IP authoring uses dependency graph to avoid circular references
omnistudio-omniscript-generate Provides namespace and element dependency data OmniScript authoring uses namespace-correct field names
omnistudio-flexcard-generate Provides namespace and data source dependency map FlexCard authoring uses detected IP references for validation
external-diagram-mermaid-generate Consumes dependency graph for visualization This skill generates Mermaid output compatible with external-diagram-mermaid-generate styling
platform-custom-object-generate / platform-custom-field-generate Provides sObject metadata for Data Mapper analysis Object field validation during dependency extraction
platform-metadata-deploy Deployment uses namespace-correct metadata types This skill provides the correct metadata type names per namespace

Gotchas

Scenario Handling
Mixed namespace org (migration in progress) Probe all three namespaces; report if multiple return results. Components may exist under both old and migrated namespaces.
Inactive components with dependencies Include in dependency graph but mark as inactive. Warn if active component depends on inactive one.
Large orgs (1000+ components) Use SOQL pagination (LIMIT/OFFSET or queryMore). Process in batches of 200.
PropertySetConfig exceeds SOQL field length Use Tooling API or REST API to fetch full JSON body for elements with truncated config.
Circular dependency detected Log the cycle path (A → B → C → A), mark all participating edges, continue traversal for remaining branches.
Components referencing deleted items Record as "broken reference" in output. Flag for cleanup.
Version conflicts (multiple active versions) Only the highest active version number participates in runtime. Warn if lower versions have unique dependencies.

Notes

  • Dependencies: Requires sf CLI with org authentication. Optional: external-diagram-mermaid-generate for styled visualization.
  • Namespace must be detected first: All downstream queries depend on knowing the correct object and field API names.
  • PropertySetConfig is the key: Nearly all dependency information lives in this JSON field on OmniProcessElement records.
  • DataSourceConfig for FlexCards: Data sources are in DataSourceConfig, NOT a Definition field (which does not exist on OmniUiCard). Card layout/states are in PropertySetConfig.
  • Data Mapper items contain object references: InputObjectName and OutputObjectName on OmniDataTransformItem records reveal which sObjects a Data Mapper reads from and writes to. The foreign key to the parent is OmniDataTransformationId (full "Transformation").
  • IsIntegrationProcedure is the discriminator: OmniProcess uses a boolean IsIntegrationProcedure field, not a TypeCategory field (which does not exist). The OmniProcessType picklist is computed from this boolean and is useful for filtering reads but cannot be set directly on create.
  • sf data create record limitations: The --values flag cannot handle JSON strings in textarea fields (e.g., PropertySetConfig). Use sf api request rest --method POST --body @file.json instead for records with JSON configuration.
  • Related skills: omnistudio-datamapper-generate, omnistudio-integration-procedure-generate, omnistudio-omniscript-generate, omnistudio-flexcard-generate — install these to enable the full OmniStudio authoring suite

Reference File Index

File When to read
references/namespace-guide.md Phase 1 — complete object/field mapping across all three namespaces (Core, vlocity_cmt, vlocity_ins), metadata type names for deployment, mixed-namespace migration scenarios
references/dependency-patterns.md Phase 3 — complete dependency extraction rules per element type, FlexCard data source parsing, Data Mapper item parsing, circular reference detection algorithm, impact analysis patterns
专用于Salesforce CME EPC产品目录建模,支持Product2记录创建、属性配置、Offer捆绑包构建及DataPack JSON元数据审查。适用于产品属性分配与价格条目管理,排除OmniScript设计及Apex逻辑实现。
用户创建或更新Product2 EPC记录 配置AttributeAssignment负载或属性元数据 构建包含Product Child Items的Offer捆绑包 审查EPC DataPack JSON元数据以进行产品目录变更
skills/omnistudio-epc-catalog-generate/SKILL.md
npx skills add forcedotcom/sf-skills --skill omnistudio-epc-catalog-generate -g -y
SKILL.md
Frontmatter
{
    "name": "omnistudio-epc-catalog-generate",
    "metadata": {
        "version": "1.0"
    },
    "description": "Salesforce Industries CME EPC product-modeling skill for Product2-based catalog creation. Use when creating EPC products, configuring product attributes, building offer bundles with Product Child Items, or reviewing EPC DataPack JSON metadata for product catalog changes. TRIGGER when: user creates or updates Product2 EPC records, AttributeAssignment payloads, AttributeMetadata\/AttributeDefaultValues, Offer bundles, or ProductChildItem relationships. DO NOT TRIGGER when: designing OmniScripts\/FlexCards\/Integration Procedures (use omnistudio-omniscript-generate, omnistudio-flexcard-generate, or omnistudio-integration-procedure-generate), implementing Apex business logic (use platform-apex-generate), or troubleshooting deployment pipelines (use platform-metadata-deploy)."
}

omnistudio-epc-catalog-generate: CME EPC Product and Offer Modeling

Expert Salesforce Industries CME EPC modeler for creating Product2-based catalog entries, assigning configurable attributes, and building offer bundles through Product Child Item relationships.

This skill is optimized for DataPack-style metadata authoring. Use the canonical template set in assets/:

  • assets/product2-offer-template.json
  • assets/attribute-assignment-template.json
  • assets/product-child-item-template.json
  • assets/pricebook-entries-template.json
  • assets/price-list-entries-template.json
  • assets/object-field-attributes-template.json
  • assets/orchestration-scenarios-template.json
  • assets/decomposition-relationships-template.json
  • assets/compiled-attribute-overrides-template.json
  • assets/override-definitions-template.json
  • assets/parent-keys-template.json

Additional packaged examples are available under assets/examples/, organized by offer type:

  • assets/examples/samsung-galaxy-s22-bundle/ — bundle offer example
  • assets/examples/business-internet-premium-fttc-simple-offer/ — simple offer example
  • assets/examples/business-internet-pro-vpl-simple-offer/ — simple offer example
  • assets/examples/static-ip-simple-offer/ — simple offer example

The examples/business-internet-plus-bundle/ folder contains a generated bundle example with a step-by-step transcript.

The root assets/ folder contains the canonical baseline template set for bundle authoring.


Scope

  • In scope: Creating and reviewing EPC Product2 records, Product Child Items, attribute metadata, offer bundles, pricing entries, decomposition and orchestration artifacts, and DataPack JSON payloads
  • Out of scope: OmniScript/FlexCard/Integration Procedure design (use omnistudio-omniscript-generate, omnistudio-flexcard-generate, or omnistudio-integration-procedure-generate), Apex business logic implementation (use platform-apex-generate), deployment pipeline troubleshooting (use platform-metadata-deploy)

Quick Reference

  • Primary object: Product2 (EPC product and offer records)
  • Attribute data: %vlocity_namespace%__AttributeMetadata__c, %vlocity_namespace%__AttributeDefaultValues__c, and %vlocity_namespace%__AttributeAssignment__c
  • Offer bundle composition: %vlocity_namespace%__ProductChildItem__c
  • Offer marker: %vlocity_namespace%__SpecificationType__c = "Offer" and %vlocity_namespace%__SpecificationSubType__c = "Bundle"
  • Companion bundle artifacts: pricebook entries, price list entries, object field attributes, orchestration scenarios, decomposition relationships, compiled attribute overrides, override definitions, and parent keys

Scoring: 120 points across 6 categories.
Thresholds: >= 95 Deploy-ready | 70-94 Needs review | < 70 Block and fix.

Glossary: EPC = Enterprise Product Catalog | CME = Communications, Media & Energy | DataPack = Vlocity JSON deployment artifact | PCI = ProductChildItem


Asset Template Set

Use the root assets/ templates when creating a bundle payload:

  • product2-offer-template.json
  • attribute-assignment-template.json
  • product-child-item-template.json
  • pricebook-entries-template.json
  • price-list-entries-template.json
  • object-field-attributes-template.json
  • orchestration-scenarios-template.json
  • decomposition-relationships-template.json
  • compiled-attribute-overrides-template.json
  • override-definitions-template.json
  • parent-keys-template.json

For additional real-world variants, use the per-example folders under assets/examples/.


Core Responsibilities

  1. Product Creation: Create EPC Product2 records with consistent naming, lifecycle dates, status, and classification fields.
  2. Attribute Modeling: Define category-based attributes, defaults, valid value sets, display sequences, and required flags.
  3. Offer Bundle Modeling: Compose offers with child products using %vlocity_namespace%__ProductChildItem__c records and clear quantity rules.
  4. Companion Metadata Generation: Generate and align all related bundle files (pricing, object field attributes, orchestration/decomposition, overrides, parent keys) from the same offer baseline.
  5. DataPack Consistency: Keep record source keys, global keys, lookup objects, and namespace fields internally consistent for deployment.

Invocation Rules (Mandatory)

Route to this skill whenever the prompt intent matches either of these:

  1. Create a product bundle:

    • User asks to create/build/generate/model an EPC offer bundle.
    • User asks for Product2 offer setup with Product Child Items.
    • User asks to generate bundle DataPack JSON artifacts from templates/examples.
  2. Score or review an existing product bundle:

    • User asks to score/assess/validate/audit an existing EPC bundle.
    • User asks to apply the 120-point rubric to existing Product2/ProductChildItem (PCI)/attribute payloads.
    • User asks for risk findings, quality gaps, or fix recommendations on bundle metadata.

Instruction priority: treat these two intents as direct triggers for omnistudio-epc-catalog-generate, even if the prompt is brief and does not mention EPC by name.


Workflow (Create/Review)

Phase 0: Prerequisites

Before proceeding, verify:

  1. Salesforce Industries org with EPC enabled
  2. Authenticated org alias in sf CLI — run sf org display --target-org <alias> to confirm
  3. Namespace model identified: %vlocity_namespace%, vlocity_cmt, or Core

If any prerequisite is unmet, ask the user to supply the org alias or namespace before continuing.


Phase 1: Identify Catalog Intent

Ask for:

  • Product type: spec product or offer bundle
  • Domain taxonomy: Family, Type/SubType, category path, and channel
  • Attribute requirements: required/optional, picklist values, default values
  • Bundle composition: child products, quantity constraints, optional vs required
  • Target org namespace model: %vlocity_namespace%, vlocity_cmt, or Core

Idempotency check: If a ProductCode is provided, verify no matching Product2 already exists before generating artifacts:

sf data query --query "SELECT Id, Name, ProductCode FROM Product2 WHERE ProductCode = '<code>'" --target-org <alias>

If a match is found, ask the user whether this is a net-new record or an update to the existing one before continuing.

Phase 1A: Clarifying Questions for Complete Bundle (Mandatory)

Before generating a new offer bundle payload, ask clarifying questions until all required inputs are known.

Required clarification checklist:

  1. Offer identity
    • What is the offer name and ProductCode?
    • Is this net-new or an update to an existing Product2 offer?
  2. Catalog classification
    • What are Family, Type/SubType, and channel/sales context values?
    • Should SpecificationType=Offer and SpecificationSubType=Bundle be set now?
  3. Lifecycle and availability
    • What are EffectiveDate and SellingStartDate?
    • Should IsActive and %vlocity_namespace%__IsOrderable__c be true at creation time?
  4. Child product composition
    • Which child products are included (name/code for each)?
    • For each child, what are required/optional semantics and sequence order?
  5. Quantity behavior per child
    • What are MinQuantity, MaxQuantity, and default Quantity?
    • Should %vlocity_namespace%__MinMaxDefaultQty__c be enforced for each line?
  6. Attribute model
    • Which attributes are required vs optional?
    • What are valid values, defaults, display types, and display sequences?
  7. Pricing and companion artifacts
    • Should pricebook and price list entries be generated now?
    • Should orchestration/decomposition/override/parent-key files be included in the same request?
  8. Namespace and keying
    • Which namespace convention should be used (%vlocity_namespace%, vlocity_cmt, or Core)?
    • Are there existing global keys/source keys to preserve?

If any required checklist item is unanswered, do not generate final bundle files yet; ask focused follow-up questions first.

Phase 2: Build Product2 Backbone

For every new EPC record, define:

  • Name
  • ProductCode (unique, stable, environment-agnostic)
  • %vlocity_namespace%__GlobalKey__c (stable UUID-style key)
  • %vlocity_namespace%__SpecificationType__c and %vlocity_namespace%__SpecificationSubType__c
  • %vlocity_namespace%__Status__c and date fields (EffectiveDate, SellingStartDate)
  • IsActive and %vlocity_namespace%__IsOrderable__c

Use assets/product2-offer-template.json as baseline structure.

Phase 3: Add Attributes

When attributes are required:

  1. Populate %vlocity_namespace%__AttributeMetadata__c category and productAttributes records.
  2. Populate %vlocity_namespace%__AttributeDefaultValues__c with attribute code to default value mapping.
  3. Create %vlocity_namespace%__AttributeAssignment__c records with:
    • category linkage
    • attribute linkage
    • UI display type (dropdown, etc.)
    • valid values and default marker

Use assets/attribute-assignment-template.json as the assignment baseline.

Phase 4: Build Offer Bundles

For offers:

  1. Keep parent Product2 record as offer (SpecificationType=Offer, SpecificationSubType=Bundle).
  2. Create root %vlocity_namespace%__ProductChildItem__c row (IsRootProductChildItem=true).
  3. Add child rows per component with:
    • parent and child references
    • sequence and line number
    • min/max/default quantity behavior (MinMaxDefaultQty, MinQuantity, MaxQuantity, Quantity)
  4. Use override rows only when behavior differs from inherited/default behavior.

Use assets/product-child-item-template.json for child relationship structure.

For complete bundle payloads, also align and include:

  • assets/pricebook-entries-template.json
  • assets/price-list-entries-template.json
  • assets/object-field-attributes-template.json
  • assets/orchestration-scenarios-template.json
  • assets/decomposition-relationships-template.json
  • assets/compiled-attribute-overrides-template.json
  • assets/override-definitions-template.json
  • assets/parent-keys-template.json

Phase 4B: Generate Companion Metadata Files

When the user asks to generate a bundle, generate/update all companion files together as one coherent set:

  1. pricebook-entries-template.json and price-list-entries-template.json
    • Keep Product2 GlobalKey/ProductCode references aligned with the parent offer.
  2. object-field-attributes-template.json
    • Keep object class references and field mappings aligned with the same offer model.
  3. orchestration-scenarios-template.json and decomposition-relationships-template.json
    • Keep decomposition and orchestration artifacts consistent with bundle child items.
  4. compiled-attribute-overrides-template.json and override-definitions-template.json
    • Keep override keys and references aligned with attribute metadata and assignments.
  5. parent-keys-template.json
    • Keep parent linkage values synchronized with generated artifact keys.

Mandatory rule: do not generate only a partial subset when a full bundle payload is requested unless the user explicitly asks for a limited file scope.

Phase 5: Validate and Handoff

Read assets/completion-block-template.txt and fill in each field to produce the handoff summary block.


Output Expectations

For a full offer bundle request, the following files are produced:

File pattern Content
*_DataPack.json Product2 offer record
*_AttributeAssignments.json Attribute category and assignment payloads
*_ProductChildItems.json Root and child ProductChildItem (PCI) rows
*_PricebookEntries.json Standard and custom pricebook entries
*_PriceListEntries.json Price list entries
*_ObjectFieldAttributes.json Object field mapping
*_OrchestrationScenarios.json Orchestration metadata
*_DecompositionRelationships.json Decomposition metadata
*_CompiledAttributeOverrides.json Compiled attribute override payload
*_OverrideDefinitions.json Override definition payload
*_ParentKeys.json Parent key linkage

For spec product (non-bundle) requests, only the DataPack, AttributeAssignments, PricebookEntries, and PriceListEntries files are required.

If generation of any file fails, stop immediately. List every file successfully generated so far and instruct the user to delete the partial set before retrying the full bundle — partial bundles cause GlobalKey mismatches on DataPack import. Do not generate the remaining files until the user confirms the partial set has been removed and a fresh attempt can begin.


Gotchas

Issue Resolution
Attribute default not in valid values list Ensure the default value exists inside the values[] array — cart will reject invalid defaults at runtime
Root ProductChildItem row missing Offer bundle traversal breaks without IsRootProductChildItem=true — always create the root row first
Mixed namespace convention in one payload Pick one namespace style (%vlocity_namespace% vs vlocity_cmt) and apply it consistently across all files in the bundle
Duplicate display sequences in same attribute category UI ordering conflict — use spaced values (10, 20, 30) to allow future inserts without collisions
ProductCode contains environment suffix Breaks cross-org references — remove _DEV, _UAT, _PROD suffixes
Companion files generated with different offer names Key mismatches break DataPack import — generate all companion files from the same baseline offer name and GlobalKey
DataPack import fails with Key not found error A lookup object reference points to a GlobalKey absent in the target org — verify GlobalKey alignment across all companion files before import
DataPack import rolls back silently Add --verbose during deployment and inspect the log for the specific record and field that triggered the rollback
Namespace mismatch between files in same bundle Mixed %vlocity_namespace% and vlocity_cmt styles in one payload cause field resolution failures — enforce a single namespace style throughout

Generation Guardrails (Mandatory)

If any anti-pattern appears, stop and ask for confirmation before proceeding.

Anti-pattern Why it fails Required correction
Missing ProductCode or unstable code values Breaks quote/cart references and package diffs Use deterministic code convention
Hardcoded org-specific IDs in relationships Fails across orgs/environments Use lookup objects with matching keys/global keys
Offer bundle without root PCI row Runtime bundle traversal issues Add root %vlocity_namespace%__ProductChildItem__c
Attribute defaults not present in valid values Invalid cart configuration defaults Ensure default exists in allowed value set
Duplicate display sequences in same attribute category UI ordering conflict Enforce unique and spaced sequence values
Offer marked active with incomplete child references Broken bundle at runtime Complete and validate child link set before activation
Mixed naming styles (snake_case, ad hoc abbreviations) Reduces maintainability and discoverability Enforce naming convention from references doc

Scoring Model (120 Points)

Read references/scoring-model.md for the full 6-category rubric and per-category criteria.

Category Points
Catalog Identity and Naming 20
EPC Product Structure 20
Attribute Modeling 25
Offer Bundle Composition 25
DataPack Integrity 15
Documentation and Handoff 15
Total 120

CLI and Validation Commands

Read scripts/cli-validation-commands.sh for sf CLI queries to inspect and validate EPC artifacts in your org. Replace <org> with your authenticated org alias before running.


Sample Skill Invocation Commands

Read scripts/sample-invocations.sh for example invocations covering common EPC modeling tasks. Replace cursor-agent with your local agent command wrapper if different.


Reference File Index

File When to read
assets/product2-offer-template.json Phase 2 — baseline structure for every new Product2 offer record
assets/attribute-assignment-template.json Phase 3 — attribute assignment structure
assets/product-child-item-template.json Phase 4 — root and child PCI row structure
assets/pricebook-entries-template.json Phase 4B — pricebook entry companion file
assets/price-list-entries-template.json Phase 4B — price list entry companion file
assets/object-field-attributes-template.json Phase 4B — object field mapping companion file
assets/orchestration-scenarios-template.json Phase 4B — orchestration scenarios companion file
assets/decomposition-relationships-template.json Phase 4B — decomposition relationships companion file
assets/compiled-attribute-overrides-template.json Phase 4B — compiled attribute overrides companion file
assets/override-definitions-template.json Phase 4B — override definitions companion file
assets/parent-keys-template.json Phase 4B — parent keys companion file
assets/completion-block-template.txt Phase 5 — handoff summary block template
assets/examples/samsung-galaxy-s22-bundle/ Phase 4 — bundle offer example; load *_DataPack.json and *_ProductChildItems.json first, then companion files as needed
assets/examples/business-internet-premium-fttc-simple-offer/ Phase 4 — simple offer (FTTC) example; load *_DataPack.json and *_AttributeAssignments.json first
assets/examples/business-internet-premium-fttc-simple-offer/Business-Internet-Premium-FTTC_RuleAssignments.json Phase 4 — FTTC offer rule assignment example; load when modeling rule-based attribute constraints
assets/examples/business-internet-pro-vpl-simple-offer/ Phase 4 — simple offer (Pro VPL) example; load *_DataPack.json and *_AttributeAssignments.json first
assets/examples/static-ip-simple-offer/ Phase 4 — simple offer (Static IP) example; load *_DataPack.json and *_AttributeAssignments.json first
examples/business-internet-plus-bundle/ Phase 4 — generated bundle example with step-by-step transcript; load TRANSCRIPT.md first, then specific JSON files referenced in it
references/epc-field-guide.md Phase 2 & 3 — EPC field-level guidance and common pitfalls
references/naming-conventions.md Phase 2 & 3 — naming and keying conventions
references/scoring-model.md Phase 5 — full 6-category scoring rubric with per-category criteria
scripts/cli-validation-commands.sh Phase 5 — sf CLI queries for validating EPC artifacts in org
scripts/sample-invocations.sh On Start — reference example invocations for common EPC tasks

Cross-Skill Integration

From Skill To omnistudio-epc-catalog-generate When
omnistudio-dependencies-analyze -> omnistudio-epc-catalog-generate Need current dependency and namespace inventory first
platform-custom-object-generate / platform-custom-field-generate -> omnistudio-epc-catalog-generate Need object or field readiness before EPC modeling
platform-soql-query -> omnistudio-epc-catalog-generate Need existing catalog query analysis
From omnistudio-epc-catalog-generate To Skill When
omnistudio-epc-catalog-generate -> omnistudio-omniscript-generate Configure guided selling UX using the modeled catalog
omnistudio-epc-catalog-generate -> omnistudio-integration-procedure-generate Build server-side orchestration over product and pricing payloads
omnistudio-epc-catalog-generate -> platform-metadata-deploy Deploy validated catalog metadata

External References

Local references:


Notes

  • This skill is intentionally DataPack-first and optimized for vlocity/Product2/... artifact authoring.
  • Keep %vlocity_namespace% placeholders intact in templates to preserve portability.
  • Prefer creating reusable spec products first, then assemble offers via child relationships.

用于创建和验证OmniStudio FlexCard,支持数据源绑定、布局设计及130分评分标准检查。适用于构建UI卡片、配置Integration Procedure数据源或审查FlexCard定义。
用户创建FlexCards 配置数据源 设计卡片布局 询问OmniUiCard元数据
skills/omnistudio-flexcard-generate/SKILL.md
npx skills add forcedotcom/sf-skills --skill omnistudio-flexcard-generate -g -y
SKILL.md
Frontmatter
{
    "name": "omnistudio-flexcard-generate",
    "metadata": {
        "version": "1.0"
    },
    "description": "OmniStudio FlexCard creation and validation with 130-point scoring. Use when building at-a-glance UI cards, configuring data source bindings to Integration Procedures, or reviewing existing FlexCard definitions for accessibility and performance. TRIGGER when: user creates FlexCards, configures data sources, designs card layouts, or asks about OmniUiCard metadata. DO NOT TRIGGER when: building OmniScripts (use omnistudio-omniscript-generate), creating Integration Procedures (use omnistudio-integration-procedure-generate), or analyzing dependencies (use omnistudio-dependencies-analyze)."
}

omnistudio-flexcard-generate: OmniStudio FlexCard Creation and Validation

Expert OmniStudio engineer specializing in FlexCard UI components for Salesforce Industries. Generate production-ready FlexCard definitions that display at-a-glance information with declarative data binding, Integration Procedure data sources, conditional rendering, and proper SLDS (Salesforce Lightning Design System) styling. All FlexCards are validated against a 130-point scoring rubric across 7 categories.

Scope

  • In scope: Creating and validating OmniStudio FlexCard definitions (OmniUiCard); configuring Integration Procedure data sources; designing card layouts, states, and action buttons; scoring against the 130-point rubric; deployment and activation
  • Out of scope: Building OmniScripts (use omnistudio-omniscript-generate), creating Integration Procedures (use omnistudio-integration-procedure-generate), mapping full dependency trees (use omnistudio-dependencies-analyze), deploying metadata to org (use platform-metadata-deploy)

Core Responsibilities

  1. FlexCard Authoring: Design and build FlexCard definitions with proper layout, states, and field mappings
  2. Data Source Binding: Configure Integration Procedure data sources with correct field mapping and error handling
  3. Test Generation: Validate cards against multiple data states (populated, empty, error, multi-record)
  4. Documentation: Produce deployment-ready documentation with data source lineage and action mappings

Document Map

Need Document Description
Best practices references/best-practices.md Layout patterns, SLDS, accessibility, performance
Data binding references/data-binding-guide.md IP sources, field mapping, conditional rendering

CRITICAL: Orchestration Order

FlexCards sit at the presentation layer of the OmniStudio stack. Ensure upstream components exist before building a FlexCard that depends on them.

omnistudio-dependencies-analyze → omnistudio-datamapper-generate → omnistudio-integration-procedure-generate → omnistudio-omniscript-generate → omnistudio-flexcard-generate (you are here)

FlexCards consume data from Integration Procedures and can launch OmniScripts. Build the data layer first, then the presentation layer.


Key Insights

Insight Detail
Configuration fields OmniUiCard uses DataSourceConfig for data source bindings and PropertySetConfig for card layout, states, and actions. There is NO Definition field on OmniUiCard in Core namespace.
Data source binding Data sources bind to Integration Procedures for live data; the IP must be active and deployed before the FlexCard can retrieve data
Child card embedding FlexCards can embed other FlexCards as child cards, enabling composite layouts with shared or independent data sources
OmniScript launching FlexCards can launch OmniScripts via action buttons, passing context data from the card's data source into the OmniScript's input
Designer virtual object The FlexCard Designer uses OmniFlexCardView as a virtual list object (/lightning/o/OmniFlexCardView/home), separate from the OmniUiCard sObject where card records are stored. Cards created via API may not appear in "Recently Viewed" until opened in the Designer.

Workflow (5-Phase Pattern)

Phase 1: Requirements Gathering

Before building, clarify these with the stakeholder:

Question Why It Matters
What is the card's purpose? Determines layout type and data density
Which data sources are needed? Identifies required Integration Procedures
What object context does it run in? Determines record-level vs. list-level display
What actions should the card expose? Drives button/link configuration and OmniScript integration
What layout best fits the use case? Single card, list, tabbed, or flyout
Are there conditional display rules? Fields or sections that appear/hide based on data values

Phase 2: Design & Layout

Read references/best-practices.md for layout patterns, SLDS compliance, accessibility requirements, and performance guidance before designing.

Card Layout Options

Layout Type Use Case Description
Single Card Record summary One card displaying fields from a single record
Card List Related records Repeating cards bound to an array data source
Tabbed Card Multi-context Multiple states displayed as tabs within one card
Flyout Card Detail on demand Expandable detail panel triggered from a summary card

Data Source Configuration

Each FlexCard data source connects to an Integration Procedure (or other source type) and maps response fields to display elements.

FlexCard → Data Source (type: IntegrationProcedure)
         → IP Name + Input Mapping
         → Response Field Mapping → Card Elements
  • Map IP response fields to card display elements using {datasource.fieldName} merge syntax
  • Configure input parameters to pass record context (e.g., {recordId}) to the IP
  • Set data source order when multiple sources feed the same card

Action Button Design

Action Type Purpose Configuration
Launch OmniScript Start a guided process OmniScript Type + SubType, pass context params
Navigate Go to record or URL Record ID or URL template with merge fields
Custom Action Platform event, LWC, etc. Custom action handler with payload mapping

Conditional Visibility

  • Show/hide fields based on data values using visibility conditions
  • Show/hide entire card states based on data source results
  • Display empty-state messaging when data source returns no records

Phase 3: Generation & Validation

Read references/data-binding-guide.md for merge field syntax, data source types, and multi-source coordination before generating. Read references/scoring-rubric.md for the full point-by-point breakdown when running the 130-point validation.

  1. Generate the FlexCard definition JSON
  2. Validate all data source references resolve to active Integration Procedures
  3. Run the 130-point scoring rubric (see Scoring section below)
  4. Verify merge field syntax matches IP response structure
  5. Check accessibility attributes on all interactive elements

Phase 4: Deployment

  1. Ensure all upstream Integration Procedures are deployed and active
  2. Run a dry-run check: use the platform-metadata-deploy skill with --dry-run before committing
  3. Deploy the FlexCard metadata (OmniUiCard) — sf project deploy start is safe to re-run; it upserts existing records
  4. Activate the FlexCard in the target org
  5. Embed the FlexCard in the target Lightning page, OmniScript, or parent FlexCard
  6. If deploy fails: check error output for specific cause — common issues: upstream IP not deployed (Cannot find OmniIntegrationProcedure), missing namespace prefix (Entity not found), or FlexCard still in Draft status (activate before retrieving)

Phase 5: Testing

Test each FlexCard against multiple data scenarios:

Scenario What to Verify
Populated data All fields render correctly, merge fields resolve
Empty data Empty-state message displays, no broken merge fields
Error state Graceful handling when IP returns an error or times out
Multi-record Card list renders correct number of items, pagination works
Action buttons OmniScript launches with correct pre-populated data
Conditional fields Visibility rules toggle correctly based on data values
Mobile Card layout adapts to smaller viewport widths

Generation Guardrails

Avoid these patterns when generating FlexCard definitions:

Anti-Pattern Why It's Wrong Correct Approach
Referencing non-existent IP data sources Card fails to load data at runtime Verify IP exists and is active before binding
Hardcoded colors in styles Breaks SLDS theming and dark mode Use SLDS design tokens and CSS custom properties
Missing accessibility attributes Fails WCAG compliance Add aria-label, role, and keyboard handlers
Excessive nested child cards Performance degrades with deep nesting Limit to 2 levels of nesting; flatten where possible
Ignoring empty states Broken UI when data source returns no records Configure explicit empty-state messaging
Hardcoded record IDs Card breaks across environments Use merge fields and context-driven parameters

Scoring Rubric (130 Points)

All FlexCards are validated against 7 categories. Thresholds: ✅ 90+ (Deploy) | ⚠️ 67-89 (Review) | ❌ <67 (Block - fix required)

Category Points Criteria
Design & Layout 25 Appropriate layout type, logical field grouping, responsive design, consistent spacing, clear visual hierarchy
Data Binding 20 Correct IP references, proper merge field syntax, input parameter mapping, multi-source coordination
Actions & Navigation 20 Action buttons configured correctly, OmniScript launch params mapped, navigation targets valid, action labels descriptive
Styling 20 SLDS tokens used (no hardcoded colors), consistent typography, proper use of card/tile patterns, dark mode compatible
Accessibility 15 aria-label on interactive elements, keyboard navigable actions, sufficient color contrast, screen reader friendly field labels
Testing 15 Verified with populated data, empty state, error state, multi-record scenario, and mobile viewport
Performance 15 Data source calls minimized, child card nesting limited (max 2 levels), no redundant IP calls, lazy loading for non-visible states

Read references/scoring-rubric.md for the full per-criterion breakdown of all 7 categories.


CLI Commands

Read scripts/flexcard-commands.sh for all FlexCard CLI commands (query, retrieve, deploy). Replace <org> with your org alias and <Name> with the FlexCard API name.


Data Source Binding

FlexCard Data Source Configuration

The DataSourceConfig field on OmniUiCard contains the data source bindings as JSON. The PropertySetConfig field contains the card layout, states, and field definitions.

IMPORTANT: There is NO Definition field on OmniUiCard in Core namespace. Use DataSourceConfig for data sources and PropertySetConfig for layout.

Read assets/omni-ui-card.json for the complete OmniUiCard record template including the DataSourceConfig JSON structure.

Data Source Types

Type dataSource.type When to Use
Integration Procedure IntegrationProcedures (plural, capital P) Primary pattern; calls an IP for live data
SOQL SOQL Direct query (use sparingly; prefer IP for abstraction)
Apex Remote ApexRemote Custom Apex class invocation
REST REST External API call via Named Credential
Custom Custom Custom data provider (pass JSON body directly)

Field Mapping from IP Response

Map IP response fields to card display elements using merge field syntax:

IP Response:                    FlexCard Merge Field:
─────────────                   ─────────────────────
{ "Name": "Acme Corp" }   →    {Name}
{ "Account": {            →    {Account.Name}
    "Name": "Acme Corp"
  }
}
{ "records": [             →    {records[0].Name}  (single)
    { "Name": "Acme" }          or iterate with Card List layout
  ]
}

Input Parameter Mapping

Pass context from the hosting page into the IP data source:

Context Variable Source Example
{recordId} Current record page Pass to IP to query related data
{userId} Running user Filter data by current user
{param.customKey} URL parameter or parent card Pass from parent FlexCard or URL

Cross-Skill Integration

Skill Relationship to omnistudio-flexcard-generate
omnistudio-integration-procedure-generate Build the IP data sources that FlexCards consume
omnistudio-omniscript-generate Build the OmniScripts that FlexCard action buttons launch
omnistudio-datamapper-generate Build DataRaptors/DataMappers that IPs use under the hood
omnistudio-dependencies-analyze Analyze dependency chains across FlexCards, IPs, and OmniScripts
platform-metadata-deploy Deploy FlexCard metadata along with upstream dependencies
experience-lwc-generate Build custom LWC components embedded within FlexCards

Gotchas

Scenario Handling
Empty data Configure an explicit empty-state with a user-friendly message; do not show raw "No data" or blank card
Error states Display a meaningful error message when the IP data source fails; log the error for debugging
Mobile responsiveness Use single-column layout for mobile; avoid horizontal scrolling; test at 320px viewport width
Long text values Truncate with ellipsis and provide a flyout or tooltip for full text
Large record sets Use card list with pagination; limit initial load to 10-25 records
Null field values Use conditional visibility to hide fields with null values rather than showing empty labels
Mixed data freshness When multiple data sources have different refresh rates, display a "last updated" indicator

FlexCard vs LWC Decision Guide

Factor FlexCard LWC
Build method Declarative (drag-and-drop) Code (JS, HTML, CSS)
Data binding Integration Procedure merge fields Wire service, Apex, GraphQL
Best for At-a-glance information display Complex interactive UIs
Testing Manual + data state verification Jest unit tests + manual
Customization Limited to OmniStudio framework Full platform flexibility
Reuse Embed as child cards Import as child components
When to choose Standard card layouts with IP data Custom behavior, animations, complex state

Dependencies

Required: Target org with OmniStudio (Industries Cloud) license, sf CLI authenticated For Data Sources: Active Integration Procedures deployed to the target org For Actions: Active OmniScripts deployed (if action buttons launch OmniScripts) Scoring: Block deployment if score < 67

Idempotency: sf project deploy start upserts metadata — safe to re-run without creating duplicates. Query first to confirm current state: see scripts/flexcard-commands.sh.

Namespace handling: In managed-package orgs, the metadata type may be prefixed (e.g., omnistudio__OmniUiCard). Check sfdx-project.json for the namespace. See scripts/flexcard-commands.sh for the namespaced deploy command.

Creating FlexCards programmatically: Use REST API (sf api request rest --method POST --body @file.json). Required fields: Name, VersionNumber, OmniUiCardType (e.g., Child). Set DataSourceConfig (JSON string) for data source bindings and PropertySetConfig (JSON string) for card layout. The sf data create record --values flag cannot handle JSON in textarea fields. Activate by updating IsActive=true after creation.


Output Expectations

Deliverables produced by this skill:

  • FlexCard JSON definition (assets/omni-ui-card.json template) — OmniUiCard record ready for REST API creation or metadata deployment
  • Data source binding blockDataSourceConfig JSON mapping Integration Procedure inputs and response fields to card elements
  • Card layout configPropertySetConfig JSON defining card states, field display, conditional visibility, and action buttons
  • Validation report — 130-point score across 7 categories with deploy/review/block threshold result
  • Deployment checklist — confirms upstream IPs are active, FlexCard is activated, and embedded in target Lightning page or parent FlexCard

External References

  • OmniStudio FlexCards (Trailhead) — Official learning module for FlexCard fundamentals and guided setup
  • OmniStudio Developer Guide — Technical reference for FlexCard metadata, data source configuration, and component properties
  • Salesforce Industries Documentation — FlexCard configuration guide covering layout, states, and actions

Reference File Index

File When to read
assets/omni-ui-card.json Phase 3 — Generation: OmniUiCard record template including DataSourceConfig JSON structure
references/best-practices.md Phase 2 — Layout patterns, SLDS compliance, accessibility requirements, and performance guidance
references/data-binding-guide.md Phase 2-3 — Data source types, merge field syntax, input parameter mapping, and multi-source coordination
references/scoring-rubric.md Phase 3 — Full per-criterion breakdown of all 7 scoring categories (130 points)
scripts/flexcard-commands.sh Phase 4 — All CLI commands for querying, retrieving, and deploying FlexCard metadata
用于创建和验证OmniStudio集成过程(IP),结合数据映射、Apex远程操作及HTTP调用。支持110分评分体系,确保依赖有效与错误处理完善。适用于服务端流程编排构建。
用户需要创建Integration Procedures 添加Data Mapper步骤 配置Remote Actions 审查现有IP配置
skills/omnistudio-integration-procedure-generate/SKILL.md
npx skills add forcedotcom/sf-skills --skill omnistudio-integration-procedure-generate -g -y
SKILL.md
Frontmatter
{
    "name": "omnistudio-integration-procedure-generate",
    "metadata": {
        "version": "1.0"
    },
    "description": "OmniStudio Integration Procedure creation and validation with 110-point scoring. Use this skill when building server-side process orchestrations that combine Data Mapper actions, Apex Remote Actions, HTTP callouts, and conditional logic. TRIGGER when: user creates Integration Procedures, adds Data Mapper steps, configures Remote Actions, or reviews existing IP configurations. DO NOT TRIGGER when: building OmniScripts (use omnistudio-omniscript-generate), creating Data Mappers directly (use omnistudio-datamapper-generate), or analyzing cross-component dependencies (use omnistudio-dependencies-analyze)."
}

omnistudio-integration-procedure-generate: OmniStudio Integration Procedure Creation and Validation

Expert OmniStudio Integration Procedure (IP) builder with deep knowledge of server-side process orchestration. Create production-ready IPs that combine DataRaptor/Data Mapper actions, Apex Remote Actions, HTTP callouts, conditional logic, and nested procedure calls into declarative multi-step operations.

Scope

  • In scope: Creating well-structured Integration Procedures from requirements; selecting and wiring element types (DataRaptor, Remote Action, HTTP, Conditional Block, Loop, Set Values, nested IP); dependency validation; error handling patterns; 110-point scoring; deployment and activation
  • Out of scope: Building OmniScripts (use omnistudio-omniscript-generate), creating Data Mappers directly (use omnistudio-datamapper-generate), designing FlexCards (use omnistudio-flexcard-generate), mapping full dependency trees (use omnistudio-dependencies-analyze), deploying metadata to org (use platform-metadata-deploy)

Required Inputs

  • Purpose: What business process is this IP orchestrating? (e.g., "onboard a new account", "process an order")
  • Target objects / data sources: Which Salesforce objects, external APIs, or both?
  • Type / SubType naming: PascalCase pair that uniquely identifies the IP (e.g., Type=OrderProcessing, SubType=Standard)
  • Target org alias: Authenticated org alias for deployment (e.g., myDevOrg)

Quick Reference

Scoring: 110 points across 6 categories. Thresholds: ✅ 90+ (Deploy) | ⚠️ 67-89 (Review) | ❌ <67 (Block - fix required)


Core Responsibilities

  1. IP Generation: Create well-structured Integration Procedures from requirements, selecting correct element types and wiring inputs/outputs
  2. Element Composition: Assemble DataRaptor actions, Remote Actions, HTTP callouts, conditional blocks, loops, and nested IP calls into coherent orchestrations
  3. Dependency Analysis: Validate that referenced DataRaptors, Apex classes, and nested IPs exist and are active before deployment
  4. Error Handling: Enforce try/catch patterns, conditional rollback, and response validation across all data-modifying steps (DML — Data Manipulation Language)

CRITICAL: Orchestration Order

omnistudio-dependencies-analyze -> omnistudio-datamapper-generate -> omnistudio-integration-procedure-generate -> omnistudio-omniscript-generate -> omnistudio-flexcard-generate (you are here: omnistudio-integration-procedure-generate)

Data Mappers referenced by the IP must exist FIRST. Build and deploy DataRaptors/Data Mappers before the IP that calls them. The IP must be active before any OmniScript or FlexCard can invoke it.


Key Insights

Insight Details
Chaining IPs call other IPs via Integration Procedure Action elements. Output of one step feeds input of the next via response mapping. Design data flow linearly where possible.
Response Mapping Each element's output is namespaced under its element name in the response JSON. Use %elementName:keyPath% syntax to reference upstream outputs in downstream inputs.
Caching IPs support platform cache for read-heavy orchestrations. Set cacheType and cacheTTL in the procedure's PropertySet. Avoid caching procedures that perform DML.
Versioning Type/SubType pairs uniquely identify an IP. Use SubType for versioning (e.g., Type=AccountOnboarding, SubType=v2). Only one version can be active at a time per Type/SubType.

Core Namespace Discriminator: OmniStudio Core stores both Integration Procedures and OmniScripts in the OmniProcess table. Use IsIntegrationProcedure = true or OmniProcessType = 'Integration Procedure' to filter IPs. Without a filter, queries return mixed results.

CRITICAL — Creating IPs via Data API: When creating OmniProcess records, set IsIntegrationProcedure = true to make the record an Integration Procedure. The OmniProcessType picklist is computed from this boolean and cannot be set directly. Also, Name is a required field on OmniProcess (not documented in standard OmniStudio docs). Use sf api request rest --method POST --body @file.json for creation — the sf data create record --values flag cannot handle JSON textarea fields like PropertySetConfig.


Workflow Design (5-Phase Pattern)

Phase 1: Requirements Gathering

Before building, evaluate alternatives: Sometimes a single DataRaptor, an Apex service, or a Flow is the better choice. IPs are optimal when you need declarative multi-step orchestration with branching, error handling, and mixed data sources.

Ask the user to gather:

  • Purpose and business process being orchestrated
  • Target objects and data sources (Salesforce objects, external APIs, or both)
  • Type/SubType naming (e.g., Type=OrderProcessing, SubType=Standard)
  • Target org alias for deployment

Then: Check existing IPs via CLI query (see CLI Commands below), identify reusable DataRaptors/Data Mappers, and review dependent components with omnistudio-dependencies-analyze.

Phase 2: Design & Element Selection

Element Type Use Case PropertySet Key
DataRaptor Extract Action Read Salesforce data bundle
DataRaptor Load Action Write Salesforce data bundle
DataRaptor Transform Action Data shaping/mapping bundle
Remote Action Call Apex class method remoteClass, remoteMethod
Integration Procedure Action Call nested IP ipMethod (format: Type_SubType)
HTTP Action External API callout path, method
Conditional Block Branching logic --
Loop Block Iterate over collections --
Set Values Assign variables/constants --

Naming Convention: [Type]_[SubType] using PascalCase. Element names within the IP should describe their action clearly (e.g., GetAccountDetails, ValidateInput, CreateOrderRecord).

Data Flow: Design the element chain so each step's output feeds naturally into the next step's input. Map outputs explicitly rather than relying on implicit namespace merging.

Phase 3: Generation & Validation

Build the IP definition with:

  • Correct Type/SubType assignment
  • Ordered element chain with explicit input/output mappings
  • Error handling on all data-modifying elements
  • Conditional blocks for branching logic

Validation (STRICT MODE):

  • BLOCK: Missing Type/SubType, circular IP calls, DML without error handling, references to nonexistent DataRaptors/Apex classes
  • WARN: Unbounded extracts without LIMIT, missing caching on read-only IPs, hardcoded IDs in PropertySetConfig, unused elements, missing element descriptions

Validation Report Format (6-Category Scoring 0-110): see assets/scoring-report-format.txt for the exact output layout.

Generation Guardrails (MANDATORY)

Anti-Pattern Impact Correct Pattern
Circular IP calls (A calls B calls A) Infinite loop / stack overflow Map dependency graph; no cycles allowed
DML without error handling Silent data corruption Wrap DataRaptor Load in try/catch or conditional error check
Unbounded DataRaptor Extract Governor limits / timeout Set LIMIT on extracts; paginate large datasets
Hardcoded Salesforce IDs in PropertySetConfig Deployment failure across orgs Use input variables, Custom Settings, or Custom Metadata
Sequential calls that could be parallel Unnecessary latency Group independent elements; no serial dependency needed
Missing response validation Downstream null reference errors Check element response before passing to next step

DO NOT generate anti-patterns even if explicitly requested.

Phase 4: Deployment

  1. Deploy prerequisite DataRaptors/Data Mappers FIRST using platform-metadata-deploy
  2. Deploy the Integration Procedure: sf project deploy start -m OmniIntegrationProcedure:<Name> -o <org>
  3. Activate the IP in the target org (set IsActive=true)
  4. Verify activation via CLI query

Phase 5: Testing

Test each element individually before testing the full chain:

  1. Unit: Invoke each DataRaptor independently, verify Apex Remote Action responses
  2. Integration: Run the full IP with representative input JSON, verify output structure
  3. Error paths: Test with invalid input, missing records, API failures to verify error handling
  4. Bulk: Test with collection inputs to verify loop and batch behavior
  5. End-to-end: Invoke the IP from its consumer (OmniScript, FlexCard, or API) and verify the full round-trip

Scoring Breakdown

110 points across 6 categories:

Design & Structure (20 points)

Criterion Points Description
Type/SubType naming 5 Follows convention, descriptive, versioned appropriately
Element naming 5 Clear, action-oriented names on all elements
Data flow clarity 5 Linear or well-documented branching; explicit input/output mapping
Element ordering 5 Logical execution sequence; no unnecessary dependencies

Data Operations (25 points)

Criterion Points Description
DataRaptor references valid 5 All referenced bundles exist and are active
Extract operations bounded 5 LIMIT set on all extracts; pagination for large datasets
Load operations validated 5 Input data validated before DML; required fields checked
Response mapping correct 5 Outputs correctly mapped between elements
Data transformation accuracy 5 Transform actions produce expected output structure

Error Handling (20 points)

Criterion Points Description
DML error handling 8 All DataRaptor Load actions have error handling
HTTP error handling 4 All HTTP actions check status codes and handle failures
Remote Action error handling 4 Apex exceptions caught and surfaced
Rollback strategy 4 Multi-step DML has conditional rollback or compensating actions

Performance (20 points)

Criterion Points Description
No unbounded queries 5 All extracts have reasonable LIMIT values
Caching applied 5 Read-only procedures use platform cache where appropriate
Parallel execution 5 Independent elements not serialized unnecessarily
No redundant calls 5 Same data not fetched multiple times across elements

Security (15 points)

Criterion Points Description
No hardcoded IDs 5 IDs passed as input variables or from metadata
No hardcoded credentials 5 API keys/tokens use Named Credentials or Custom Settings
Input validation 5 User-supplied input sanitized before use in queries or DML

Documentation (10 points)

Criterion Points Description
Procedure description 3 Clear description of purpose and business context
Element descriptions 4 Each element has a description explaining its role
Input/output documentation 3 Expected input JSON and output JSON structure documented

CLI Commands

Read scripts/cli-commands.sh before querying or deploying Integration Procedures — it contains all SOQL queries and sf project deploy/retrieve commands ready to adapt.

Core Namespace Note: The IsIntegrationProcedure=true filter is REQUIRED (or equivalently OmniProcessType='Integration Procedure'). OmniScript and Integration Procedure records share the OmniProcess sObject. Without this filter, queries return both types and produce misleading results.


Cross-Skill Integration

From Skill To omnistudio-integration-procedure-generate When
omnistudio-dependencies-analyze -> omnistudio-integration-procedure-generate "Analyze dependencies before building IP"
omnistudio-datamapper-generate -> omnistudio-integration-procedure-generate "DataRaptor/Data Mapper is ready, wire it into IP"
platform-apex-generate -> omnistudio-integration-procedure-generate "Apex Remote Action class deployed, configure in IP"
From omnistudio-integration-procedure-generate To Skill When
omnistudio-integration-procedure-generate -> platform-metadata-deploy "Deploy IP to target org"
omnistudio-integration-procedure-generate -> omnistudio-omniscript-generate "IP is active, build OmniScript that calls it"
omnistudio-integration-procedure-generate -> omnistudio-flexcard-generate "IP is active, build FlexCard data source"
omnistudio-integration-procedure-generate -> omnistudio-dependencies-analyze "Verify IP dependency graph before deployment"

Edge Cases

Scenario Solution
IP calls itself (direct recursion) Block at design time; circular dependency check is mandatory
IP calls IP that calls original (indirect recursion) Map full call graph; omnistudio-dependencies-analyze detects cycles
DataRaptor not yet deployed Deploy DataRaptors first; IP deployment will fail on missing references
External API timeout Set timeout values on HTTP Action elements; implement retry logic or graceful degradation
Large collection input to Loop Block Set batch size; test with realistic data volumes to avoid CPU timeout
Type/SubType collision with existing IP Query existing IPs before creating; SubType versioning avoids collisions
Mixed namespace (Vlocity vs Core) Confirm org namespace; element property names differ between packages

Debug: IP not executing -> check IsActive flag + Type/SubType match | Elements skipped -> verify conditional block logic + input data shape | Timeout -> check DataRaptor query scope + HTTP timeout settings | Deployment failure -> verify all referenced components deployed and active


Output Expectations

Deliverables produced by this skill:

  • Integration Procedure JSON (assets/omni-process-ip.json template) — OmniProcess record ready for REST API creation with IsIntegrationProcedure=true
  • Element JSON records (assets/omni-process-element-dr-extract.json, assets/omni-process-element-set-values.json templates) — OmniProcessElement records for each action step with PropertySetConfig wired
  • Validation report — 110-point score across 6 categories with deploy/review/block threshold result
  • Deployment checklist — confirms prerequisite DataRaptors are active, IP is activated, and consuming OmniScript or FlexCard can invoke it

Notes

API: Latest (check current Salesforce release notes; was 66.0 at time of authoring) | Mode: Strict (warnings block) | Scoring: Block deployment if score < 67

Dependencies (optional): platform-metadata-deploy, omnistudio-datamapper-generate, omnistudio-dependencies-analyze

Creating IPs programmatically: Use REST API (sf api request rest --method POST --body @file.json). Required fields: Name, Type, SubType, Language, VersionNumber, IsIntegrationProcedure=true. Then create OmniProcessElement child records for each action step (also via REST API for JSON PropertySetConfig). Activate by setting IsActive=true after all elements are created.


Reference File Index

File When to read
assets/omni-process-ip.json Phase 3 — Generation: use as the OmniProcess record template when creating the Integration Procedure via REST API
assets/omni-process-element-dr-extract.json Phase 3 — Generation: use as the DataRaptor Extract Action element template; adapt for other DR action types
assets/omni-process-element-set-values.json Phase 3 — Generation: use as the Set Values element template for variable assignment steps
assets/scoring-report-format.txt Phase 3 — Validation: use as the output layout template when presenting the 110-point validation report
references/best-practices.md Phase 2-5 — Design patterns: element composition, error handling, caching, parallel execution, and security guidance
references/element-types.md Phase 2 — Element selection: read before configuring PropertySetConfig for any element type
scripts/cli-commands.sh Phase 1 & 4 — CLI queries and deploy/retrieve commands; adapt by replacing <Name> and <org> placeholders
用于创建和验证OmniStudio OmniScript,支持多步表单、交互流程及集成编排。提供120分评分标准,涵盖元素设计、依赖分析及数据流追踪。适用于构建无代码引导式数字体验,需先完成前置依赖组件开发。
用户要求创建或生成OmniScript 设计OmniScript的步骤流程 配置OmniScript的元素类型及属性 审查现有OmniScript配置并评估得分
skills/omnistudio-omniscript-generate/SKILL.md
npx skills add forcedotcom/sf-skills --skill omnistudio-omniscript-generate -g -y
SKILL.md
Frontmatter
{
    "name": "omnistudio-omniscript-generate",
    "metadata": {
        "version": "1.0"
    },
    "description": "OmniStudio OmniScript creation and validation with 120-point scoring. Use when building guided digital experiences, multi-step forms, or interactive processes that orchestrate Integration Procedures and Data Mappers. TRIGGER when: user creates OmniScripts, designs step flows, configures element types, or reviews existing OmniScript configurations. DO NOT TRIGGER when: building FlexCards (use omnistudio-flexcard-generate), creating Integration Procedures directly (use omnistudio-integration-procedure-generate), or analyzing dependencies (use omnistudio-dependencies-analyze)."
}

omnistudio-omniscript-generate: OmniStudio OmniScript Creation and Validation

Expert OmniStudio OmniScript builder for declarative, step-based guided digital experiences. OmniScripts are the OmniStudio analog of Screen Flows: multi-step, interactive processes that collect input, orchestrate server-side logic (Integration Procedures, DataRaptors), and present results to the user — all without code.

Quick Reference

Scoring: 120 points across 6 categories. Thresholds: ✅ 90+ (Deploy) | ⚠️ 67-89 (Review) | ❌ <67 (Block - fix required)


Scope

  • In scope: Creating OmniScripts from requirements, element selection and PropertySetConfig design, dependency analysis (Integration Procedures, DataRaptors), data flow tracing, 120-point validation scoring, deployment and activation
  • Out of scope: Building FlexCards (use omnistudio-flexcard-generate), creating Integration Procedures directly (use omnistudio-integration-procedure-generate), mapping full dependency trees (use omnistudio-dependencies-analyze), deploying metadata to org (use platform-metadata-deploy)

Required Inputs

Gather these before building:

Input Description Default
Type Process category (e.g., ServiceRequest, Enrollment) None — required
SubType Specific variation (e.g., NewCase, UpdateAddress) None — required
Language Locale for the OmniScript English
Purpose Business process this OmniScript guides None — required
Target org Org alias for deployment Current default org
Data sources Objects/APIs to query or update Identify from requirements

Core Responsibilities

  1. OmniScript Generation: Create well-structured OmniScripts from requirements, selecting appropriate element types for each step
  2. Element Design: Configure PropertySetConfig JSON for each element with correct data binding, validation, and conditional logic
  3. Dependency Analysis: Map all references to Integration Procedures, DataRaptors, and embedded OmniScripts before deployment
  4. Data Flow Analysis: Trace data through the OmniScript JSON structure — from prefill through user input to final save actions

CRITICAL: Orchestration Order

omnistudio-dependencies-analyze → omnistudio-datamapper-generate → omnistudio-integration-procedure-generate → omnistudio-omniscript-generate → omnistudio-flexcard-generate (you are here: omnistudio-omniscript-generate)

OmniScripts consume Integration Procedures and DataRaptors. Build those FIRST. FlexCards may launch OmniScripts — build FlexCards AFTER. Use omnistudio-dependencies-analyze to map the full dependency tree before starting.


Key Insights

Insight Details
Type/SubType/Language triplet Uniquely identifies an OmniScript. All three values are required and form the composite key. Example: Type=ServiceRequest, SubType=NewCase, Language=English
PropertySetConfig JSON blob containing all element configuration — layout, data binding, validation rules, conditional visibility. This is where the real logic lives
Core namespace OmniProcess with IsIntegrationProcedure = false (equivalently OmniProcessType='OmniScript'). Elements are child OmniProcessElement records
Element hierarchy Elements use Level/Order fields for tree structure. Level 0 = Steps, Level 1+ = elements within steps. Order determines sequence within a level
Version management Multiple versions can exist; only one can be active per Type/SubType/Language triplet. Activate via the IsActive field
Data JSON OmniScripts pass a single JSON data structure through all steps. Elements read from and write to this shared JSON via merge field syntax

Workflow Design (5-Phase Pattern)

Phase 1: Requirements Gathering

Before building, evaluate alternatives: OmniScripts are best for complex, multi-step guided processes. For simple single-screen data entry, consider Screen Flows. For data display without interaction, consider FlexCards.

Ask the user to gather:

  • Type: The process category (e.g., ServiceRequest, Enrollment, ClaimSubmission)
  • SubType: The specific variation (e.g., NewCase, UpdateAddress, FileAppeal)
  • Language: Typically English unless multi-language support is required
  • Purpose: What business process this OmniScript guides the user through
  • Target org: Org alias for deployment
  • Data sources: Which objects/APIs need to be queried or updated

Then: Check existing OmniScripts to avoid duplication, identify reusable Integration Procedures or DataRaptors, and map the dependency chain.

Phase 2: Design & Element Selection

Design each step and select element types appropriate to the interaction pattern.

Container Elements

Element Type Purpose Key Config
Step Top-level container for a group of UI elements; each Step is a page in the wizard chartLabel, knowledgeOptions, show (conditional visibility)
Conditional Block Show/hide a group of elements based on conditions conditionType, show expression
Loop Block Iterate over a data list and render elements for each item loopData (JSON path to array)
Edit Block Inline editing container for tabular data editFields, dataSource

Input Elements

Element Type Purpose Key Config
Text Single-line text input label, placeholder, pattern (regex validation)
Text Area Multi-line text input label, maxLength, rows
Number Numeric input with optional formatting label, min, max, step, format
Date Date picker label, dateFormat, minDate, maxDate
Date/Time Date and time picker label, dateFormat, timeFormat
Checkbox Boolean toggle label, defaultValue
Radio Radio button group for single selection label, options (static or data-driven)
Select Dropdown selection label, options, optionSource (static/data)
Multi-select Multiple item selection label, options, maxSelections
Type Ahead Search/autocomplete input label, dataSource, searchField, minCharacters
Signature Signature capture pad label, penColor, backgroundColor
File File upload label, maxFileSize, allowedExtensions
Currency Currency input with locale formatting label, currencyCode, min, max
Email Email input with format validation label, placeholder
Telephone Phone number input with masking label, mask, placeholder
URL URL input with format validation label, placeholder
Password Masked text input label, minLength
Range Slider input label, min, max, step
Time Time picker label, timeFormat

Display Elements

Element Type Purpose Key Config
Text Block Static content display (HTML supported) textContent, HTMLTemplateId
Headline Section heading text, level (h1-h6)
Aggregate Calculated summary display aggregateExpression, format
Disclosure Expandable/collapsible content label, defaultExpanded
Image Image display imageURL, altText
Chart Data visualization chartType, dataSource

Action Elements

Element Type Purpose Key Config
DataRaptor Extract Action Pull data from Salesforce bundle, inputMap, outputMap
DataRaptor Load Action Push data to Salesforce bundle, inputMap
Integration Procedure Action Call server-side Integration Procedure ipMethod (Type_SubType), inputMap, outputMap, remoteOptions
Remote Action Call Apex @RemoteAction or REST remoteClass, remoteMethod, inputMap
Navigate Action Page navigation or redirection targetType, targetId, URL
DocuSign Envelope Action Trigger DocuSign envelope templateId, recipientMap
Email Action Send email emailTemplateId, recipientMap

Logic Elements

Element Type Purpose Key Config
Set Values Variable assignment and data transformation elementValueMap (key-value pairs)
Validation Input validation rules with custom messages validationFormula, errorMessage
Formula Calculate values using formula expressions expression, dataType
Submit Action Final submission of collected data postMessage, preTransformBundle, postTransformBundle

Phase 3: Generation & Validation

Run scripts/check-duplicate-omniscript.sh <Type> <SubType> <Language> <org> to verify no duplicate Type/SubType/Language exists.

Build the OmniScript:

  1. Create the OmniProcess record with Type, SubType, Language, and OmniProcessType='OmniScript'
  2. Create OmniProcessElement child records for each Step (Level=0)
  3. Create OmniProcessElement child records for each element within Steps (Level=1+, ordered by Order field)
  4. Configure PropertySetConfig JSON for each element
  5. Wire action elements to their Integration Procedures / DataRaptors

Validation (STRICT MODE):

  • BLOCK: Missing Type/SubType/Language, circular OmniScript embedding, broken IP/DataRaptor references, missing required PropertySetConfig fields
  • WARN: Steps with no elements, input elements without validation, missing error handling on actions, unused data paths, deeply nested elements (>4 levels)

Validation Report Format (6-Category Scoring 0-120):

Score: 102/120 ---- Very Good
-- Design & Structure: 22/25 (88%)
-- Data Integration: 18/20 (90%)
-- Error Handling: 17/20 (85%)
-- Performance: 18/20 (90%)
-- User Experience: 17/20 (85%)
-- Security: 10/15 (67%)

Phase 4: Deployment

  1. Prerequisites: Verify org auth (sf org display -o <org>). Confirm all referenced DataRaptors and Integration Procedures are active in the target org.
  2. Deploy all dependencies first: DataRaptors, Integration Procedures, referenced OmniScripts.
  3. Run scripts/deploy-omniscript.sh <Name> <Type> <SubType> <org> — this deploys the OmniScript and verifies activation. If deployment fails, the script outputs recovery instructions (deactivate and delete the partial record, then retry).
  4. Activate the OmniScript version after successful deployment if not auto-activated.

Phase 5: Testing

Walk through all paths with various data scenarios:

  • Happy path: Complete all steps with valid data, verify submission
  • Validation testing: Submit invalid data at each input, verify error messages
  • Conditional testing: Exercise all conditional blocks and verify show/hide logic
  • Data prefill: Verify DataRaptor Extract Actions populate elements correctly
  • Save for later: Test resume functionality if enabled
  • Navigation: Test back/forward/cancel behavior across all steps
  • Error scenarios: Simulate IP/DataRaptor failures, verify error handling
  • Embedded OmniScripts: Test data passing between parent and child OmniScripts
  • Bulk data: Test with large datasets in Loop Blocks and Type Ahead elements

Rules / Constraints

Anti-Pattern Impact Correct Pattern
Circular OmniScript embedding Infinite rendering loop Map dependency tree; never embed A in B if B embeds A
Unbounded DataRaptor Extract Performance degradation Add filter conditions; limit returned records
Missing input validation Bad data entry Add Validation elements or pattern/required on inputs
Hardcoded Salesforce IDs Deployment failure across orgs Use merge fields or Custom Settings/Metadata
Integration Procedure (IP) Action without error handling Silent failures Configure showError, errorMessage in PropertySetConfig
Large images in Text Blocks Slow page load Use Image elements with optimized URLs
Too many elements per Step Poor user experience Limit to 7-10 input elements per Step
Missing conditional visibility Irrelevant fields shown Use show expressions to hide inapplicable elements

Do not generate anti-patterns even if explicitly requested.


Scoring: 120 Points Across 6 Categories

Design & Structure (25 points)

Check Points Criteria
Type/SubType/Language set correctly 5 All three fields populated with meaningful values
Step organization 5 Logical grouping, 7-10 elements per step max
Element naming 5 Descriptive names following PascalCase convention
Conditional logic 5 Proper use of Conditional Blocks and show expressions
Version management 5 Clean version history, only one active version

Data Integration (20 points)

Check Points Criteria
DataRaptor references valid 5 All Extract/Load bundles exist and are active
Integration Procedure references valid 5 All IP actions reference active IPs
Input/Output maps correct 5 Data flows correctly between elements and actions
Data prefill configured 5 Initial data loaded before user interaction

Error Handling (20 points)

Check Points Criteria
Action elements have error handling 5 showError configured on all IP/DR actions
User-facing error messages 5 Clear, actionable error text
Validation on required inputs 5 All required fields have validation rules
Fallback behavior defined 5 Graceful handling when data sources return empty

Performance (20 points)

Check Points Criteria
No unbounded data fetches 5 All DataRaptor Extracts have filters/limits
Lazy loading configured 5 Action elements fire on step entry, not OmniScript load
Element count per Step reasonable 5 No Step with >15 elements
Conditional rendering used 5 Elements hidden when not applicable (not just invisible)

User Experience (20 points)

Check Points Criteria
Logical step flow 5 Steps follow natural task progression
Input labels and help text 5 All inputs have clear labels and contextual help
Navigation controls 5 Back, Next, Cancel, Save for Later configured appropriately
Responsive layout 5 Elements configured for mobile and desktop breakpoints

Security (15 points)

Check Points Criteria
No sensitive data in client-side JSON 5 Passwords, SSNs, tokens kept server-side
IP actions use server-side processing 5 Sensitive logic in Integration Procedures, not client OmniScript
Field-level access respected 5 Data access matches user profile/permission set

CLI Commands

See scripts/cli-reference.sh for the full command reference. Common commands:

# List active OmniScripts
sf data query -q "SELECT Id,Name,Type,SubType,Language,IsActive,VersionNumber FROM OmniProcess WHERE IsActive=true AND OmniProcessType='OmniScript' LIMIT 50" -o <org>

# Query elements for a specific OmniScript
sf data query -q "SELECT Id,Name,ElementType,Level,Order FROM OmniProcessElement WHERE OmniProcessId='<id>' ORDER BY Level,Order LIMIT 200" -o <org>

# Check OmniScript versions
sf data query -q "SELECT Id,VersionNumber,IsActive,LastModifiedDate FROM OmniProcess WHERE Type='<Type>' AND SubType='<SubType>' AND OmniProcessType='OmniScript' ORDER BY VersionNumber DESC LIMIT 10" -o <org>

Cross-Skill Integration

From Skill To omnistudio-omniscript-generate When
omnistudio-dependencies-analyze -> omnistudio-omniscript-generate "Analyze dependencies before building OmniScript"
omnistudio-datamapper-generate -> omnistudio-omniscript-generate "DataRaptor ready, build the OmniScript that uses it"
omnistudio-integration-procedure-generate -> omnistudio-omniscript-generate "IP ready, wire it into the OmniScript action"
From omnistudio-omniscript-generate To Skill When
omnistudio-omniscript-generate -> omnistudio-flexcard-generate "Build FlexCard that launches this OmniScript"
omnistudio-omniscript-generate -> platform-metadata-deploy "Deploy OmniScript to target org"
omnistudio-omniscript-generate -> omnistudio-dependencies-analyze "Map full dependency tree before deployment"
omnistudio-omniscript-generate -> omnistudio-integration-procedure-generate "Need a new IP for this OmniScript action"
omnistudio-omniscript-generate -> omnistudio-datamapper-generate "Need a DataRaptor for data prefill"

Gotchas

Issue Resolution
Multi-language OmniScript Create separate versions per Language with shared Type/SubType; use translation workbench for labels
Embedded OmniScript data passing Map parent data JSON keys to child OmniScript input via prefillJSON; test data round-trip
Large Loop Block datasets Paginate or limit DataRaptor results; consider server-side filtering in Integration Procedure (IP)
OmniScript in FlexCard flyout Ensure FlexCard passes required context data; test flyout sizing
Community/Experience Cloud deployment Verify OmniScript component is available in Experience Builder; check guest user permissions
Save & Resume (Save for Later) Configure saveNameTemplate, saveExpireInDays; test resume with partial data
Versioning conflicts Deactivate old version before activating new; never have two active versions for same Type/SubType/Language triplet
Custom LWC in OmniScript Register LWC as OmniScript-compatible; follow omniscript-lwc namespace conventions
Namespaced orgs If deploying into a managed OmniStudio package org, prefix bundle names and API names with the appropriate namespace (e.g., omnistudio__)
OmniProcessType cannot be set on create OmniProcessType is computed from IsIntegrationProcedure (false for OmniScripts); do not set it directly

For common runtime troubleshooting (element not rendering, data not prefilling, IP action failing silently), see references/best-practices.md Section 8.


Notes

API: 66.0 | Mode: Strict (warnings block) | Scoring: Block deployment if score < 67

Required upstream skills: omnistudio-datamapper-generate, omnistudio-integration-procedure-generate

Optional skills: platform-metadata-deploy, omnistudio-flexcard-generate, omnistudio-dependencies-analyze

Creating OmniScripts programmatically: Use REST API (sf api request rest --method POST --body @file.json). Required fields: Name, Type, SubType, Language, VersionNumber. OmniScripts default to IsIntegrationProcedure=false — do NOT set OmniProcessType directly (it is computed). The sf data create record --values flag cannot handle JSON textarea fields like PropertySetConfig. Create child OmniProcessElement records via REST API for each Step and element.


Output Expectations

Deliverables produced by this skill:

  • OmniScript JSON (assets/omni-process-omniscript.json template) — OmniProcess record ready for REST API creation
  • Step element JSON (assets/omni-process-element-step.json template) — OmniProcessElement record for each Step (Level=0)
  • Element JSON (assets/omni-process-element-text-block.json and similar) — OmniProcessElement records for child elements (Level=1+)
  • Validation report — 120-point score across 6 categories with pass/warn/block threshold result

Reference File Index

File When to read
references/element-types.md Phase 2 — Element selection: read before configuring PropertySetConfig for any element type
references/best-practices.md Phase 2-5 — Design patterns: read for step design, data prefill, validation, navigation, performance, and troubleshooting guidance
assets/omni-process-omniscript.json Phase 3 — Generation: use as the OmniProcess record template when building the OmniScript via REST API
assets/omni-process-element-step.json Phase 3 — Generation: use as the Step (Level=0) OmniProcessElement record template
assets/omni-process-element-text-block.json Phase 3 — Generation: use as the Text Block element template; adapt for other display element types
scripts/check-duplicate-omniscript.sh Phase 3 — Run before creating a new OmniScript to verify no duplicate Type/SubType/Language exists
scripts/deploy-omniscript.sh Phase 4 — Run to deploy OmniScript and verify activation; includes prerequisite checks and error recovery
scripts/cli-reference.sh Any phase — Full CLI command reference for querying, retrieving, deploying, and verifying OmniScripts
配置 Salesforce 组织是否接收 Transactable Marketplace 合作伙伴报价。通过 Metadata API 修改 Org Preference,支持查询当前状态、写入设置文件、部署变更及验证结果,适用于开启或关闭该功能场景。
用户请求启用或禁用合作伙伴报价接收 配置 TransactableMarketplaceReceivePartnerOffers 偏好设置 设置市场合作伙伴报价接收功能
skills/platform-agentexchange-partner-offers-configure/SKILL.md
npx skills add forcedotcom/sf-skills --skill platform-agentexchange-partner-offers-configure -g -y
SKILL.md
Frontmatter
{
    "name": "platform-agentexchange-partner-offers-configure",
    "metadata": {
        "version": "1.0",
        "minApiVersion": "67.0"
    },
    "description": "Enable or disable the org preference that controls whether a Salesforce org can receive partner offers from the Transactable Marketplace. Use this skill when the user wants to turn partner offer reception on or off for an org. TRIGGER when: user asks to enable or disable partner offers, configure TransactableMarketplaceReceivePartnerOffers, configure enableTransactableMarketplaceReceivePartnerOffers, set up marketplace partner offer reception, toggle the TM partner offers setting, edit a TransactableMarketplacePrivateOffer.settings file, or configure org preferences related to transactable marketplace. DO NOT TRIGGER when: user needs to create or manage the partner offer records themselves, configure marketplace listing settings, or work with SfdcPartnerOffer objects (use platform-metadata-deploy or platform-apex-generate instead)."
}

Enabling Transactable Marketplace Receive Partner Offers Org Preference

This skill configures the enableTransactableMarketplaceReceivePartnerOffers org preference via the TransactableMarketplacePrivateOfferSettings Metadata API type, which controls whether a Salesforce org is eligible to receive partner offers through the Transactable Marketplace. It is required for subscriber orgs that participate in the TM partner offer flow.

Scope

  • In scope: Reading the current value of the pref, enabling or disabling it via Metadata API (TransactableMarketplacePrivateOfferSettings), and verifying the change took effect.
  • Out of scope: Creating or managing partner offer records, configuring marketplace listings, or any Apex/trigger changes related to offer processing.

Required Inputs

  • Target org alias or username: The org where the pref should be set. Ask if not provided.
  • Desired state: true (enable) or false (disable). Default: true.

Workflow

Phase 1 — Check current state

  1. Query the current preference value by running:

    sf data query -q "SELECT Preference, Value FROM OrgPreference WHERE Preference = 'TransactableMarketplaceReceivePartnerOffers'" --target-org <alias> --use-tooling-api
    

    If the record exists and Value = true, the pref is already enabled — confirm with the user before proceeding. If the query returns no rows, the pref is not yet set (defaults to false).

  2. Resolve the org's package directory to determine where to write metadata. Run this and use its output as <packageDir>:

    jq -r '.packageDirectories[0].path // "force-app/main/default"' sfdx-project.json
    

Phase 2 — Apply the preference

  1. Write the TransactableMarketplacePrivateOfferSettings metadata file — load assets/org-pref-template.md for the exact XML structure, then write the file at:

    <packageDir>/settings/TransactableMarketplacePrivateOffer.settings
    

    Set <enableTransactableMarketplaceReceivePartnerOffers>true</enableTransactableMarketplaceReceivePartnerOffers> (or false if disabling).

  2. Deploy the metadata to the target org. Before running the deploy, confirm with the user:

    • Confirmed the target org alias with the user (deploying to the wrong org is not easily reversible)
    • Confirmed the desired state (true/false) matches the user's intent
    sf project deploy start --metadata TransactableMarketplacePrivateOfferSettings --target-org <alias>
    

Phase 3 — Verify

  1. Confirm the change by re-running the Tooling API query from step 1 and verifying the Value column matches the desired state.

  2. Report to the user — see Output Expectations below.


Rules / Constraints

Rule Rationale
Always query the current value before writing metadata Avoids unnecessary deploys and detects conflicting changes
Use TransactableMarketplacePrivateOfferSettings as the metadata type This is the concrete type registered in the platform for this pref, not the generic OrgPreferenceSettings
The settings file must be named TransactableMarketplacePrivateOffer.settings Metadata API requires the filename to match the settings node name
Do not hardcode force-app/main/default/ Always read sfdx-project.json for the actual package directory
Never deploy without confirming the org alias with the user Deploying to the wrong org is not easily reversible

Gotchas

Issue Resolution
Tooling API query returns no rows Pref is unset (defaults to false). Safe to create a new settings file.
Deploy fails with INVALID_TYPE The metadata type name is TransactableMarketplacePrivateOfferSettings — check the --metadata flag value.
Deploy succeeds but value doesn't change Another settings file in the project may be overriding this one. Search for other TransactableMarketplacePrivateOffer.settings files in the project.
INSUFFICIENT_ACCESS_OR_READONLY on deploy User running the deploy must have the "Modify All Data" or org preference admin permission in the target org.
Pref not visible in UI enableTransactableMarketplaceReceivePartnerOffers is not surfaced in Setup UI — the Tooling API query is the only way to verify it.
Available from API version 67.0+ only The type is available from API v67.0 — deploying against an older API version will fail.

Output Expectations

After completing all phases, report:

Org: <alias>
Preference: enableTransactableMarketplaceReceivePartnerOffers
Previous value: <true|false|unset>
New value: <true|false>
File written: <packageDir>/settings/TransactableMarketplacePrivateOffer.settings
Deploy status: Success

Reference File Index

File When to read
assets/org-pref-template.md Phase 2, step 3 — use as the exact XML structure for the settings file
examples/org-preference-settings.xml To verify the generated file matches expected format
通过 Salesforce CLI 调用 Connect API 获取 Agentic Setup 提示词分类及嵌套提示词。适用于用户查询、列出或展示现有分类库时触发,不支持创建修改或生成代码。
用户要求获取、拉取、列出或显示 agentic setup categories 用户询问 prompt categories, setup copilot categories, prompt library categories 用户查找 available setup prompts, Agentforce prompt library, 或 copilot prompts
skills/platform-agentsetup-categories-fetch/SKILL.md
npx skills add forcedotcom/sf-skills --skill platform-agentsetup-categories-fetch -g -y
SKILL.md
Frontmatter
{
    "name": "platform-agentsetup-categories-fetch",
    "metadata": {
        "version": "1.0",
        "minApiVersion": "67.0"
    },
    "description": "Fetch agentic setup prompt categories from a connected Salesforce org using the Connect API. Use this skill to call GET \/agenticsetup\/categories and return the list of prompt categories, optionally with their nested prompts. TRIGGER when: user asks to get, fetch, list, or show agentic setup categories, prompt categories, setup copilot categories, prompt library categories, available setup prompts, Agentforce prompt library, or copilot prompts. DO NOT TRIGGER when: user wants to create new categories, work with non-categories endpoints, or generate OpenAPI specs.",
    "allowed-tools": "Bash Read"
}

platform-agentsetup-categories-fetch

Fetch prompt categories from the Agentic Setup Categories Connect API on a connected Salesforce org.

Scope

In scope:

  • Calling GET /services/data/{apiVersion}/agenticsetup/categories via SF CLI
  • Passing the fetchPrompts query parameter to include nested prompts
  • Parsing and presenting the JSON response (categories, labels, prompts)
  • Handling errors (403 when feature is disabled, auth failures)

Out of scope — delegate elsewhere:

  • Creating or modifying prompt categories → not supported via this API (read-only)
  • Org authentication setup → use sf org login separately
  • Permission set assignment → assigning-permission-set

Required Inputs

The agent needs:

  • A connected org (already authenticated via sf org login)
  • Optionally: whether to include nested prompts (fetchPrompts=true)
  • Optionally: target API version (default: v67.0). Replace v67.0 in the URL with the user-specified version if provided.

Workflow

1. Verify org connectivity

sf org display --json

Confirm the org is authenticated and extract the instance URL. If no default org is set, ask the user which org to target with --target-org.

2. Call the Categories API

Basic call (categories only):

sf api request rest /services/data/v67.0/agenticsetup/categories --method GET

With nested prompts:

sf api request rest "/services/data/v67.0/agenticsetup/categories?fetchPrompts=true" --method GET

3. Parse and present the response

The API returns a JSON object with a categories array. Each category has name, label, and prompts fields. See references/api-response-schema.md for the full response structure and examples for both fetchPrompts=true and fetchPrompts=false.

Present the results clearly:

  • List categories with their name and label
  • If fetchPrompts=true was used, show nested prompts under each category
  • Note any categories with empty prompts arrays (means no prompts exist for that category)

4. Handle errors

Error Meaning Action
Success (exit 0) 200 OK Parse and display results
FUNCTIONALITY_NOT_ENABLED Feature not enabled for this org/user Tell user the Agentic Setup Categories feature needs to be enabled — check Setup > Einstein/Agentforce
INVALID_SESSION_ID Session expired Re-authenticate with sf org login
NOT_FOUND Endpoint not found API version too old or feature not deployed to this org

Rules / Constraints

Rule Rationale
Always use sf api request rest — never curl or raw HTTP curl bypasses ~/.sfdx session tokens and requires manual Authorization headers, making it brittle
NEVER use SOQL queries Categories are NOT in standard objects — only available via this Connect REST API
NEVER generate files (LWC, Apex, XML) This is a data-fetching task, not a code generation task
Default to v67.0 unless user specifies This is the min-version where the endpoint was introduced
Don't pass fetchPrompts unless asked Reduces payload size; prompts can be large
Categories are sorted by label The API returns them alphabetically — don't re-sort
Prompts are sorted by text Within each category, prompts come alphabetically

Output Expectations

When finishing, present:

  1. Number of categories returned
  2. Category list — name and label for each
  3. Prompts (if requested) — nested under their category
  4. Any errors encountered with suggested fixes

Reference File Index

File When to read
references/api-response-schema.md When you need to understand the full response structure and field descriptions
用于生成、重构和审查生产级 Apex 代码。支持类、触发器、异步作业等类型,遵循特定分层架构与命名规范。包含发现项目约定、编写代码、自动生成测试及运行代码分析器并修复违规项的完整工作流,确保代码质量与可部署性。
用户提及 Apex、.cls 或 triggers 要求创建或重构 Service、Selector、Domain、Batch、Queueable、Schedulable、Invocable、DTO、Utility、Interface、Abstract、Exception、REST 资源等类 涉及 SObject CRUD、集合映射、获取关联记录、定时/批处理作业、触发器设计、@AuraEnabled 控制器、@RestResource 端点或自定义 REST API 请求对现有 Apex 代码进行审查
skills/platform-apex-generate/SKILL.md
npx skills add forcedotcom/sf-skills --skill platform-apex-generate -g -y
SKILL.md
Frontmatter
{
    "name": "platform-apex-generate",
    "metadata": {
        "version": "1.0",
        "minApiVersion": "66.0"
    },
    "description": "Primary Apex authoring skill for class generation, refactoring, and review. ALWAYS ACTIVATE when the user mentions Apex, .cls, triggers, or asks to create\/refactor a class (service, selector, domain, batch, queueable, schedulable, invocable, DTO, utility, interface, abstract, exception, REST resource). Use this skill for requests involving SObject CRUD, mapping collections, fetching related records, scheduled jobs, batch jobs, trigger design, @AuraEnabled controllers, @RestResource endpoints, custom REST APIs, or code review of existing Apex."
}

Generating Apex

Use this skill for production-grade Apex: new classes, selectors, services, async jobs, invocable methods, and triggers; and for evidence-based review of existing .cls OR .trigger.

Required Inputs

Gather or infer before authoring:

  • Class type (service, selector, domain, batch, queueable, schedulable, invocable, trigger, trigger action, DTO, utility, interface, abstract, exception, REST resource)
  • Target object(s) and business goal
  • Class name (derive using the naming table below)
  • Net-new vs refactor/fix; any org/API constraints
  • Deployment targets (default to runSpecifiedTests and use generated tests where applicable)

Defaults unless specified:

  • Sharing: with sharing (see sharing rules per type below)
  • Access: public (use global only when required by managed packages or @RestResource)
  • API version: 66.0 (minimum version)
  • ApexDoc comments: yes

If the user provides a clear, complete request, generate immediately without unnecessary back-and-forth.


Workflow

All steps are sequential. Do not skip, merge, or reorder. If blocked, stop and ask for missing context. If not applicable, mark N/A with a one-line justification in the report.

Phase 1 — Author

  1. Discover project conventions

    • Service-Selector-Domain layering, logging utilities
    • Existing classes/triggers and current trigger framework or handler pattern
    • Whether Trigger Actions Framework (TAF) is already in use
  2. Choose the smallest correct pattern (see Type-Specific Guidance below)

  3. Review templates and assets

    • Read the matching template from assets/ before authoring (see Type-Specific Guidance for the file mapping)
    • When a references/ example exists for the type, read it as a concrete style guide
    • For any test class work, always read and use platform-apex-test-generate skill
  4. Author with guardrails -- apply every rule in the Rules section below

    • Generate {ClassName}.cls with ApexDoc
    • Generate {ClassName}.cls-meta.xml
  5. Generate test classes -- Load the skill platform-apex-test-generate to create {ClassName}Test.cls and {ClassName}Test.cls-meta.xml. Apex tests are always required to be generated to deploy. No test file creation or edits can occur without loading the platform-apex-test-generate skill to generate tests.

Phase 2 — Validate (required before reporting)

Writing files is the midpoint, not the finish line. Steps 6 and 7 each require a tool invocation and produce output that must appear in the Step 8 report. Do not summarize or present the report until both steps have run and their output is captured.

  1. Run code analyzer

    • Invoke MCP run_code_analyzer on all generated/updated .cls files.
    • Remediate all sev0, sev1, and sev2 violations; re-run until clean.
    • Capture the final tool output verbatim for the report.
    • Fallback: sf code-analyzer run --target <target>. If both are unavailable, record run_code_analyzer=unavailable: <error> in the report.
  2. Execute Apex tests

    • Run org tests including {ClassName}Test via sf apex run test or MCP.
    • Delegate all test generation/fixes/coverage work to platform-apex-test-generate; iterate until the tests pass.
    • Capture pass/fail counts and coverage percentage for the report.
    • If unavailable, record test_execution=unavailable: <error> in the report.

Phase 3 — Report

  1. Report -- use the output format at the bottom of this file.
    • The Analyzer line must contain the actual Step 6 tool output (or run_code_analyzer=unavailable: <reason> after attempting invocation).
    • The Testing line must contain the actual Step 7 results (or test_execution=unavailable: <reason> after attempting invocation).
    • A report missing either line is incomplete. Always attempt the tool invocation before recording unavailable.

Rules

Hard-Stop Constraints (Must Enforce)

If any constraint would be violated in generated code, stop and explain the problem before proceeding:

Constraint Rationale
Place all SOQL outside loops Avoid query governor limits (100 queries)
Place all DML outside loops Avoid DML governor limits (150 statements)
Declare a sharing keyword on every class Prevent unintended without sharing defaults and data exposure
Use Custom Metadata/Labels/describe calls instead of hardcoded IDs Ensure portability across orgs
Always handle exceptions (log, rethrow, or recover) Prevent silent failures
Use bind variables for all dynamic SOQL with user input Prevent SOQL injection
Use Apex-native collections (List, Map, Set) rather than Java types Prevent compile errors
Verify methods exist in Apex before use Prevent reliance on non-existent APIs
Avoid System.debug() in main code paths Debug statements evaluate even when loggign is not active and consume CPU. Use a logging framework if required on main code paths
Never use @future methods Use Queueable with System.Finalizer; @future cannot chain, cannot be called from Batch, and cannot accept non-primitive types

Bulkification & Governor Limits

  • All public APIs accept and process collections; single-record overloads delegate to the bulk method
  • In batch/bulk flows, prefer partial-success DML (Database.update(records, false)) and process SaveResult for errors
  • Use Map<Id, SObject> constructor for efficient ID-based lookups from query results
  • Use Map<Id, List<SObject>> to group child records by parent; build the map in a single loop before processing
  • Use Set<Id> for deduplication and membership checks; prefer Set.contains() over List.contains()
  • Use relationship subqueries to fetch parent + child records in a single SOQL when both are needed
  • Use AggregateResult with GROUP BY for rollup calculations instead of querying and counting in Apex
  • Only DML records that actually changed — compare against Trigger.oldMap or prior state before adding to the update list
  • Use Limits.getQueries(), Limits.getDmlStatements(), Limits.getCpuTime() to monitor consumption in complex transactions

SOQL Optimization

  • Use selective queries with proper WHERE clauses; use indexed fields (Id, Name, OwnerId, lookup/master-detail fields, ExternalId fields, custom indexes) in filters when possible
  • SELECT * does not exist in SOQL -- always specify the exact fields needed
  • Apply LIMIT clauses to bound result sets; use ORDER BY for deterministic results
  • When querying Custom Metadata Types (objects ending with __mdt), do NOT use SOQL — use the built-in methods ({CustomMdt__mdt}.getAll().values(), getInstance(), etc.)

Caching

  • Use Platform Cache (Cache.Org / Cache.Session) for frequently accessed, rarely changed data; set a TTL and always handle cache misses — cache can be evicted at any time
  • Use private static Map fields as transaction-scoped caches to prevent duplicate queries within the same execution context; lazy-initialize on first access

Security

  • Default to with sharing; document justification for without sharing or inherited sharing
  • WITH USER_MODE in SOQL and AccessLevel.USER_MODE for Database DML for CRUD/FLS enforcement
  • Validate dynamic field/operator names via allowlist or Schema.describe
  • Named Credentials for all external credentials/API keys
  • AuraHandledException for @AuraEnabled user-facing errors (no internal details)
  • without sharing requires a Custom Permission check
  • Isolate without sharing logic in dedicated helper classes; call from with sharing entry points to limit elevated-access scope
  • Encrypt PII/sensitive data at rest via Platform Encryption; never expose PII in debug statements, error messages, or API responses

Security Verification

Before finalizing, verify: CRUD/FLS enforced (SOQL + DML) · explicit sharing keyword on every class · no hardcoded secrets or Record IDs · PII excluded from logs and error messages · error messages sanitized for end users.

Error Handling

  • Catch specific exceptions before generic Exception; include context in messages
  • Use try/catch only around code that can throw (DML, callouts, JSON parsing, casts); avoid defensive wrapping of simple assignments/collection ops/arithmetic
  • Preserve exception cause chains: new CustomException('message', cause) (do not replace stack trace with concatenated messages)
  • Provide a custom exception class per service domain when meaningful
  • In @AuraEnabled methods, catch exceptions and rethrow as AuraHandledException
  • Fallback option: when no meaningful domain exception exists, catch generic Exception and either rethrow it or wrap it in a minimal custom exception that preserves the original cause.

Null Safety

  • Add guard clauses for null/empty inputs at the top of every public method; match style to context: return early in private/trigger-handler methods, throw exceptions in public APIs, record.addError() in validation services
  • Return empty collections instead of null
  • Use safe navigation (?.) for chained property access
  • Never dereference map.get(key) inline unless presence is guaranteed; use containsKey, assignment+null check, or safe navigation first
  • Use null coalescing (??) for default values
  • Prefer String.isBlank(value) over manual checks like value == null || value.trim().isEmpty()

Constants & Literals

  • Use enums over string constants whenever possible; enum values follow UPPER_SNAKE_CASE
  • Extract repeated literal strings/numbers into private static final constants or a constants class
  • Use Label. custom labels for user-facing strings
  • Use Custom Metadata for configurable values (thresholds, mappings, feature flags)
  • Never output HTML-escaped entities in code (e.g., &#39;); use literal single quotes ' in Apex string literals

Naming Conventions

Type Pattern Example
Service {SObject}Service AccountService
Selector {SObject}Selector AccountSelector
Domain {SObject}Domain OpportunityDomain
Batch {Descriptive}Batch AccountDeduplicationBatch
Queueable {Descriptive}Queueable ExternalSyncQueueable
Schedulable {Descriptive}Schedulable DailyCleanupSchedulable
DTO {Descriptive}DTO AccountMergeRequestDTO
Wrapper {Descriptive}Wrapper OpportunityLineWrapper
Utility {Descriptive}Util StringUtil
Interface I{Descriptive} INotificationService
Abstract Abstract{Descriptive} AbstractIntegrationService
Exception {Descriptive}Exception AccountServiceException
REST Resource {SObject}RestResource AccountRestResource
Trigger {SObject}Trigger AccountTrigger
Trigger Action TA_{SObject}_{Action} TA_Account_SetDefaults

Additional naming rules:

  • Classes: PascalCase
  • Methods: camelCase, start with a verb (get, create, process, validate, is, has, can)
  • Variables: camelCase, descriptive nouns; Lists as plural nouns (e.g., accounts, relatedContacts); Maps as {value}By{key} (e.g., accountsById); Sets as {noun}Ids
  • Constants: UPPER_SNAKE_CASE
  • Use full descriptive names instead of abbreviations (acc, tks, rec)

ApexDoc

  • Required on the class header and every public/global method
  • Include: brief description, @param, @return, @throws, @example where helpful

Class-level format:

/**
 * Provides services for geolocation and address conversion.
 */
public with sharing class GeolocationService { }

Method-level format:

/**
 * @param paramName Description of the parameter
 * @return Description of the return value
 * @example
 * List<Account> results = AccountService.deduplicateAccounts(accountIds);
 */

Code Structure & Architecture

  • Single responsibility per class; max 500 lines -- split when exceeded
  • Return Early: validate preconditions at method top, return/throw immediately
  • Extract private helpers for methods over ~40 lines
  • Use Dependency Injection (constructor/method params) for testability
  • Prefer composition and narrow interfaces over deep inheritance; extend via new implementations, not modifications
  • Enforce single-level abstraction per method across layer boundaries:
Layer Owns Must NOT contain
Trigger Event routing only Business logic, orchestration
Handler/Service Flow control, coordination Inline SOQL/DML/HTTP/parsing
Domain Business rules, validation Queries, callouts, persistence details
Data/Integration SOQL, DML, HTTP Business decisions
  • Disallowed: methods mixing orchestration with inline SOQL/DML/HTTP; business rules mixed with parsing internals; validation + persistence + cross-system plumbing in one method

Async Decision Matrix

Scenario Default Key Traits
Standard async work Queueable Job ID, chaining, non-primitive types, configurable delay (up to 10 min via AsyncOptions), dedup signatures
Very large datasets Batch Apex Chunked processing, max 5 concurrent; use QueryLocator for large scopes
Modern batch alternative CursorStep (Database.Cursor) 2000-record chunks, higher throughput, no 5-job limit
Recurring schedule Scheduled Flow (preferred) or Schedulable Schedulable has 100-job limit; use only when chaining to Batch or needing complex Apex logic
Post-job cleanup Finalizer (System.Finalizer) Runs regardless of Queueable success/failure
Long-running callouts Continuation Up to 3 per transaction, 3 parallel
Delays > 10 minutes System.scheduleBatch() Schedule a Batch job at a specific future time
Legacy fire-and-forget @future Do not use in new code — see Hard-Stop Constraints; replace with Queueable + Finalizer

Type-Specific Guidance

Service

  • Template: assets/service.cls · Reference: references/AccountService.cls
  • with sharing; stateless — no public fields or mutable instance state; keep public APIs focused and static where reasonable
  • Delegate all SOQL to Selectors and SObject behavior to Domains
  • Wrap business errors in a custom exception (e.g., AccountServiceException)

Selector

  • Template: assets/selector.cls · Reference: references/AccountSelector.cls
  • inherited sharing; one per SObject or query domain
  • Return List<SObject> or Map<Id, SObject>; use a shared base field list constant (no inline duplication)
  • Accept filter parameters; always include WITH USER_MODE

Domain

  • Template: assets/domain.cls
  • with sharing; encapsulate field defaults, derivations, and validations
  • Operate on in-memory lists only; no SOQL/DML (belongs in Services/Selectors)

Batch

  • Template: assets/batch.cls · Reference: references/AccountDeduplicationBatch.cls
  • with sharing; implement Database.Batchable<SObject> (add Database.Stateful when tracking across chunks)
  • start() = query definition; execute() = business logic; finish() = logging/notification
  • Use QueryLocator for large datasets; handle partial failures via Database.SaveResult
  • Accept filter parameters via constructor for reusability

Queueable

  • Template: assets/queueable.cls
  • with sharing; implement Queueable and optionally Database.AllowsCallouts when HTTP callouts are needed
  • Accept data via constructor
  • Add chain-depth guards to prevent infinite chains
  • Optionally implement Finalizer for recovery/cleanup
  • Use AsyncOptions for configurable delay (up to 10 min) and dedup signatures

Schedulable

  • Template: assets/schedulable.cls
  • with sharing; execute() delegates to Queueable or Batch
  • Provide CRON constants and a convenience scheduleDaily() helper

DTO / Wrapper

  • Template: assets/dto.cls
  • No sharing keyword needed (pure data containers)
  • Simple public properties; no-arg + parameterized constructors; Comparable when ordering matters
  • Use @JsonAccess on private/protected inner DTOs that are serialized/deserialized

Utility

  • Template: assets/utility.cls
  • No sharing keyword needed; all methods public static; private constructor
  • Pure, side-effect-free; no SOQL/DML

Interface

  • Template: assets/interface.cls
  • Define clear contracts with ApexDoc on each method signature

Abstract

  • Template: assets/abstract.cls
  • with sharing; offer default behavior via virtual methods
  • Mark extension points protected virtual or protected abstract
  • Include a concrete example in the ApexDoc showing how to extend the class

Custom Exception

  • Template: assets/exception.cls
  • No sharing keyword; extend Exception with descriptive names
  • Supported constructors: (), ('msg'), (cause), ('msg', cause)

Trigger

  • Template: assets/trigger.cls
  • One trigger per object; delegate all logic to handler/TAF action classes
  • Include all relevant DML contexts; if TAF: new MetadataTriggerHandler().run();

Trigger Action (TAF)

  • One class per concern per context; implement TriggerAction.{Context}
  • Register via Trigger_Action__mdt (actions are inactive without registration)
  • Name: TA_{SObject}_{ActionName}; prefer field-value comparison over static booleans for recursion

Invocable Method (@InvocableMethod)

  • Template: assets/invocable.cls
  • with sharing; inner Request/Response with @InvocableVariable
  • Method must be public static; non-static or single-object signatures will not compile
  • Accept List<Request>, return List<Response>; bulkify (SOQL/DML outside loops)
  • Decorator parameters: label (required — Flow Builder display name), description, category (groups actions in Builder), callout=true (required when method makes HTTP callouts)
  • @InvocableVariable parameters: label (required), description, required=true/false
  • @InvocableVariable supports: primitives, Id, SObject, List<T> only (no Map/Set/Blob); use List<Id> or List<SObject> fields for Flow collection I/O
  • Always include isSuccess, errorMessage, and errorType (e.getTypeName()) in Response
  • Return errors in Response (recommended); throwing an exception triggers the Flow Fault path — reserve for unrecoverable failures only

REST Resource (@RestResource)

  • Template: assets/rest-resource.cls
  • global with sharing; both class and methods must be global
  • Versioned URL: @RestResource(urlMapping='/{resource}/v1/*')
  • Use proper HTTP status codes per branch (200/201/400/404/422/500); never default all errors to 500
  • Validate inputs (Id format: Pattern.matches('[a-zA-Z0-9]{15,18}', value)); bind all user input in SOQL
  • Include LIMIT/ORDER BY in queries; implement pagination (pageSize/offset)
  • Standardized ApiResponse wrapper (success, message, data/records); inner request/response DTOs
  • Thin controller: delegate business logic to Service classes

@AuraEnabled Controller

  • with sharing; use WITH USER_MODE in all SOQL
  • Use @AuraEnabled(cacheable=true) only for read-only queries; leave cacheable unset for DML operations
  • Catch exceptions and rethrow as AuraHandledException with user-friendly messages

Output Expectations

Deliverables per class:

  • {ClassName}.cls
  • {ClassName}.cls-meta.xml (default API version 66.0 or higher unless specified)
  • {ClassName}Test.cls (generated via platform-apex-test-generate skill)
  • {ClassName}Test.cls-meta.xml (generated via platform-apex-test-generate skill)

Deliverables per trigger:

  • {TriggerName}.trigger
  • {TriggerName}.trigger-meta.xml (default API version 66.0 or higher unless specified)

Meta XML template:

<?xml version="1.0" encoding="UTF-8"?>
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>{API_VERSION}</apiVersion>
    <status>Active</status>
</ApexClass>

Report in this order:

Apex work: <summary>
Files: <paths>
Design: <pattern / framework choices>
Workflow: all steps completed (1-8); any N/A justified
Risks: <security, bulkification, async, dependency notes>
Analyzer: <REQUIRED -- paste actual run_code_analyzer output or state "run_code_analyzer=unavailable: <reason>">
Testing: <REQUIRED -- paste actual test execution results (pass/fail, coverage) or state "test_execution=unavailable: <reason>">
Deploy: <dry-run or next step>

Cross-Skill Integration

Need Delegate to
Apex tests / fix failures platform-apex-test-generate skill
Describe objects/fields metadata skill (if available)
Deploy to org deploy skill (if available)
Flow calling Apex Flow skill (if available)
LWC calling Apex LWC skill (if available)

Troubleshooting Boundary

This skill handles production .cls/.trigger/.apex issues only: compile/parse failures, deployment dependency errors, runtime governor-limit failures. For test execution, assertions, coverage, or sf apex run test failures, delegate to platform-apex-test-generate.

用于生成和验证Salesforce Apex测试类,支持TestDataFactory、批量测试及Mock策略。适用于创建新测试、提升覆盖率、调试失败用例及分析覆盖率报告,禁止用于生产代码或LWC测试。
创建新的Apex测试类 提升Apex代码覆盖率 调试和修复失败的Apex测试 运行测试执行与覆盖率分析 实现触发器或服务层的测试模式
skills/platform-apex-test-generate/SKILL.md
npx skills add forcedotcom/sf-skills --skill platform-apex-test-generate -g -y
SKILL.md
Frontmatter
{
    "name": "platform-apex-test-generate",
    "metadata": {
        "version": "1.0"
    },
    "description": "Generate and validate Apex test classes with TestDataFactory patterns, bulk testing (251+ records), mocking strategies, assertion best practices, and disciplined test-fix loops. Use this skill when creating new Apex test classes, improving test coverage, debugging and fixing failing Apex tests, running test execution and coverage analysis, or implementing testing patterns for triggers, services, controllers, batch jobs, queueables, and integrations. Triggers on *Test.cls, *_Test.cls files, sf apex run test workflows, coverage reports, test-fix loops. Do NOT trigger for production Apex code (use platform-apex-generate) or Jest\/LWC tests."
}

Generating Apex Tests

Generate production-ready Apex test classes and run disciplined test-fix loops with coverage analysis.

Core Principles

  1. One behavior per method — each test method validates a single scenario. Separate positive, negative, and bulk tests. NEVER combine related-but-distinct inputs (e.g., null and empty) in one method — create _NullInput_ and _EmptyInput_ as separate test methods
  2. Bulkify tests — test with 251+ records to cross the 200-record trigger batch boundary. Batch Apex exception: in test context only one execute() invocation runs, so set batchSize >= testRecordCount. See references/async-testing.md
  3. Isolate test data — every @TestSetup must delegate record creation to a TestDataFactory class. If none exists, create one first. Never build record lists inline in @TestSetup. Never rely on org data (SeeAllData=false) or hardcoded IDs. For duplicate rule handling, see references/test-data-factory.md
  4. Assert meaningfully — use exact expected values computed from test data setup. NEVER use range assertions or approximate counts when the value is deterministic. Always include failure messages. See references/assertion-patterns.md
  5. Use Assert class onlyAssert.areEqual, Assert.isTrue, Assert.fail, etc. Never use legacy System.assert, System.assertEquals, or System.assertNotEquals
  6. Mock external boundaries — use HttpCalloutMock for callouts, Test.setFixedSearchResults for SOSL, DML mock classes for database isolation. Design for testability via constructor injection. See references/mocking-patterns.md
  7. Test negative paths — validate error handling and exception scenarios, not just happy paths
  8. Wrap with start/stop — pair Test.startTest() with Test.stopTest() to reset governor limits and force async execution

Test.startTest() / Test.stopTest()

Always wrap the code under test in Test.startTest() / Test.stopTest():

  • Resets governor limits so the test measures only the code under test
  • Executes async operations synchronously (queueables, batch, future methods)
  • Fires scheduled jobs immediately

Test Code Anti-Patterns

Anti-Pattern Fix
SOQL/DML inside loops Query once before the loop; use Map<Id, SObject> for lookups
Magic numbers in assertions Derive expected values from setup constants
God test class (>500 lines) Split into multiple test classes by behavior area
Long test methods (>30 lines) Extract Given/When/Then into helper methods
Generic Exception catch Catch the specific expected type (e.g., DmlException)

Workflow

Step 1 — Gather Context

Before generating or fixing tests, identify:

  • the target production class(es) under test
  • existing test classes, test data factories, and setup helpers
  • desired test scope (single class, specific methods, suite, or local tests)
  • coverage threshold (75% minimum for deploy, 90%+ recommended)
  • org alias when running tests against an org

Step 2 — Generate the Test Class

Apply the structure, naming conventions, and patterns from the asset templates and reference docs.

MANDATORY — File Deliverables: For every test class, create BOTH files:

  1. {ClassName}Test.cls — the test class (use assets/test-class-template.cls as starting point)
  2. {ClassName}Test.cls-meta.xml — the metadata file:
<?xml version="1.0" encoding="UTF-8"?>
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>66.0</apiVersion>
    <status>Active</status>
</ApexClass>

If no TestDataFactory exists in the project, create TestDataFactory.cls + TestDataFactory.cls-meta.xml using assets/test-data-factory-template.cls.

@TestSetup Example

@TestSetup
static void setupTestData() {
    List<Account> accounts = TestDataFactory.createAccounts(251, true);
}

Test Method Structure

Use Given/When/Then:

@isTest
static void shouldUpdateStatus_WhenValidInput() {
    // Given
    List<Account> accounts = [SELECT Id FROM Account];

    // When
    Test.startTest();
    MyService.processAccounts(accounts);
    Test.stopTest();

    // Then
    List<Account> updated = [SELECT Id, Status__c FROM Account];
    Assert.areEqual(251, updated.size(), 'All accounts should be processed');
}

Negative Test — Exception Pattern

Use try/catch with Assert.fail to verify expected exceptions:

@isTest
static void shouldThrowException_WhenInvalidInput() {
    // Given
    List<Account> emptyList = new List<Account>();

    // When/Then
    Test.startTest();
    try {
        MyService.processAccounts(emptyList);
        Assert.fail('Expected MyCustomException to be thrown');
    } catch (MyCustomException e) {
        Assert.isTrue(e.getMessage().contains('cannot be empty'),
            'Exception message should indicate empty input');
    }
    Test.stopTest();
}

Naming Convention

  • should[ExpectedResult]_When[Scenario]: shouldSendNotification_WhenOpportunityClosedWon
  • [SubjectOrAction]_[Scenario]_[ExpectedResult]: AccountUpdate_ChangeName_Success

Step 3 — Run Tests

Start narrow when debugging; widen after the fix is stable.

# Single test class
sf apex run test --class-names MyServiceTest --result-format human --code-coverage --target-org <alias>

# Specific test methods
sf apex run test --tests MyServiceTest.shouldUpdateStatus_WhenValidInput --result-format human --target-org <alias>

# All local tests
sf apex run test --test-level RunLocalTests --result-format human --code-coverage --target-org <alias>

Step 4 — Analyze Results

Focus on:

  • failing methods — exception types and stack traces
  • uncovered lines and weak coverage areas
  • whether failures indicate bad test data, brittle assertions, or broken production logic

Step 5 — Fix Loop

When tests fail, run a disciplined fix loop (max 3 iterations — stop and surface root cause if still failing):

  1. Read the failing test class and the class under test
  2. Identify root cause from error messages and stack traces
  3. Apply fix — adjust test data or assertions for test-side issues; delegate production code issues to the platform-apex-generate skill
  4. Rerun the focused test before broader regression
  5. Repeat until all tests pass, iteration limit reached, or root cause requires design change

Step 6 — Validate Coverage

Level Coverage Purpose
Production deploy 75% minimum Required by Salesforce
Recommended 90%+ Best practice target
Critical paths 100% Business-critical code

Cover all paths: positive, negative/exception, bulk (251+ records), callout/async.

What to Test by Component

Component Key Test Scenarios
Trigger Bulk insert/update/delete, recursion guard, field change detection
Service Valid/invalid inputs, bulk operations, exception handling
Controller Page load, action methods, view state
Batch start/execute/finish, scope matching (batch size >= record count), Database.Stateful tracking, error handling, chaining (separate methods — finish() calling Database.executeBatch() throws UnexpectedException)
Queueable Chaining (only first job runs in tests), bulkification, error handling, callout mocks before Test.startTest()
Callout Success response, error response, timeout
Selector Valid/null/empty inputs, bulk (251+), field population, sort order, WITH USER_MODE via System.runAs
Scheduled Direct execution via execute(null), CRON registration via CronTrigger query
Platform Event Test.enableChangeDataCapture(), Test.getEventBus().deliver(), verify subscriber side effects

Output Expectations

Deliverables per test class:

  • {ClassName}Test.cls + {ClassName}Test.cls-meta.xml (match API version of class under test; default 66.0)
  • TestDataFactory.cls + TestDataFactory.cls-meta.xml (if not already present)

Reference Files

Load on demand for detailed patterns:

Reference When to use
references/test-data-factory.md TestDataFactory patterns, field overrides, duplicate rule handling
references/assertion-patterns.md Assertion best practices, anti-patterns, common pitfalls
references/mocking-patterns.md HttpCalloutMock, DML mocking, StubProvider, SOSL, Email, Platform Events
references/async-testing.md Batch, Queueable, Future, Scheduled job testing
用于执行Apex测试、分析代码覆盖率及修复失败测试。支持测试范围发现、结果诊断与结构化修复循环,涵盖断言、批量处理及异步测试最佳实践,排除生产代码编写和LWC测试场景。
用户需要运行Apex测试 检查代码覆盖率 修复失败的测试用例 处理 *Test.cls 或 *_Test.cls 文件
skills/platform-apex-test-run/SKILL.md
npx skills add forcedotcom/sf-skills --skill platform-apex-test-run -g -y
SKILL.md
Frontmatter
{
    "name": "platform-apex-test-run",
    "metadata": {
        "version": "1.1"
    },
    "description": "Apex test execution, coverage analysis, and test-fix loops with 120-point scoring. Use when the user needs to run Apex tests, check code coverage, fix failing tests, or work with *Test.cls \/ *_Test.cls files. TRIGGER when: user runs Apex tests, checks code coverage, fixes failing tests, or touches *Test.cls \/ *_Test.cls files. DO NOT TRIGGER when: writing Apex production code (use platform-apex-generate), Agentforce agent testing (use agentforce-test), or Jest\/LWC tests (use experience-lwc-generate)."
}

platform-apex-test-run: Salesforce Test Execution & Coverage Analysis

Use this skill when the user needs Apex test execution and failure analysis: running tests, checking coverage, interpreting failures, improving coverage, and managing a disciplined test-fix loop for Salesforce code.

When This Skill Owns the Task

Use platform-apex-test-run when the work involves:

  • sf apex run test workflows
  • Apex unit-test failures
  • code coverage analysis
  • identifying uncovered lines and missing test scenarios
  • structured test-fix loops for Apex code

Delegate elsewhere when the user is:

  • writing or refactoring production Apex → platform-apex-generate skill
  • testing Agentforce agents → agentforce-test skill
  • testing LWC with Jest → experience-lwc-generate

Required Context to Gather First

Ask for or infer:

  • target org alias
  • desired test scope: single class, specific methods, suite, or local tests
  • coverage threshold expectation
  • whether the user wants diagnosis only or a test-fix loop
  • whether related test data factories already exist

Recommended Workflow

1. Discover test scope

Identify:

  • existing test classes
  • target production classes
  • test data factories / setup helpers

2. Run the smallest useful test set first

Start narrow when debugging a failure; widen only after the fix is stable.

3. Analyze results

Focus on:

  • failing methods
  • exception types and stack traces
  • uncovered lines / weak coverage areas
  • whether failures indicate bad test data, brittle assertions, or broken production logic

4. Run a disciplined fix loop

When the issue is code or test quality:

  • delegate code fixes to platform-apex-generate skill when needed
  • add or improve tests
  • rerun focused tests before broader regression

5. Improve coverage intentionally

Cover:

  • positive path
  • negative / exception path
  • bulk path (251+ records where appropriate)
  • callout or async path when relevant

High-Signal Rules

Rule Rationale
Default to SeeAllData=false Ensures test isolation; prevents reliance on org-specific data
Every test must assert meaningful outcomes Tests with no assertions prove nothing and give false confidence
Test bulk behavior with 251+ records Triggers process in batches of 200; 251 records crosses the boundary
Use factories / @TestSetup when they improve clarity Consistent data creation in one place; rolled back between test methods
Pair Test.startTest() with Test.stopTest() for async Ensures async operations (queueable, future) complete before assertions
Do not hide flaky org dependencies inside tests Prevents intermittent failures tied to org state

Gotchas

Issue Resolution
Test passes locally but fails in CI org Check for SeeAllData=true or undeclared dependencies on org-specific records
Coverage drops unexpectedly after refactor Run focused class-level tests first, then widen to RunLocalTests to confirm
"Uncommitted work pending" error in callout test DML and HTTP callouts cannot be mixed in the same test context without Test.startTest() wrapping
Mock not taking effect in test Ensure Test.setMock() is called before the code that makes the callout
@TestSetup data missing in test method @TestSetup data is committed per test method — re-query it; do not store in static variables

Output Format

When finishing, report in this order:

  1. What tests were run
  2. Pass/fail summary
  3. Coverage result
  4. Root-cause findings
  5. Fix or next-run recommendation

Suggested shape:

Test run: <scope>
Org: <alias>
Result: <passed / partial / failed>
Coverage: <percent / key classes>
Issues: <highest-signal failures>
Next step: <fix class, add test, rerun scope, or widen regression>

Cross-Skill Integration

Need Delegate to Reason
Fix production code or author test classes platform-apex-generate skill Code generation and repair
Create bulk / edge-case test data platform-data-manage Realistic test datasets
Deploy updated tests to org platform-metadata-deploy Deployment workflows
Inspect detailed runtime logs platform-apex-logs-debug Deeper failure analysis

Reference File Index

File When to read
references/cli-commands.md All sf apex run test command flags, output formats, async execution, and coverage commands
references/test-patterns.md Test class templates — basic, bulk (251+), mock callout, and data factory patterns
references/testing-best-practices.md Core testing principles — AAA pattern, naming conventions, bulk, negative, and mock strategies
references/test-fix-loop.md Agentic test-fix loop implementation and failure analysis decision tree
references/mocking-patterns.md HttpCalloutMock, DML mocking, StubProvider, and selector mocking patterns
references/performance-optimization.md Techniques to reduce test execution time — DML mocking, SOQL mocking, loop optimizations
assets/basic-test.cls Template: standard test class with @TestSetup, positive / negative / bulk / edge-case methods
assets/bulk-test.cls Template: bulk test with 251+ records that crosses the 200-record trigger batch boundary
assets/mock-callout-test.cls Template: HTTP callout mock using HttpCalloutMock
assets/test-data-factory.cls Template: reusable TestDataFactory with create and insert helpers
assets/dml-mock.cls Template: IDML interface + DMLMock implementation for database-free unit tests
assets/stub-provider-example.cls Template: StubProvider-based dependency injection stub
scripts/parse-test-results.py Post-tool hook — parses sf apex run test JSON output and formats failures for the auto-fix loop

Score Guide

Score Meaning
108+ strong production-grade test confidence
96–107 good test suite with minor gaps
84–95 acceptable but strengthen coverage / assertions
< 84 below standard; revise before relying on it
用于生成和验证Salesforce自定义字段元数据XML,特别处理Roll-up Summary和Master-Detail等高频失败类型。涵盖字段创建、约束校验及部署错误排查,但不包含Value Set定义。
创建或生成Salesforce自定义字段 配置Roll-up Summary或Master-Detail字段 解决字段部署错误(如公式、关系约束) 设置Picklist依赖或Record Type限制
skills/platform-custom-field-generate/SKILL.md
npx skills add forcedotcom/sf-skills --skill platform-custom-field-generate -g -y
SKILL.md
Frontmatter
{
    "name": "platform-custom-field-generate",
    "metadata": {
        "version": "1.0",
        "minApiVersion": "51.0"
    },
    "description": "Use this skill when users need to create, generate, or validate Salesforce Custom Field metadata. Trigger when users mention custom fields, field types, Roll-up Summary fields, Master-Detail relationships, Lookup relationships, formula fields, picklists, dependent (controlling) picklists, referencing a value set from a field, or scoping\/limiting picklist values for a specific record type. Also use when users encounter field deployment errors, especially around Roll-up Summary format, Master-Detail constraints, formula issues, or a record type that won't deploy without a business process. Use this skill for custom field metadata work, field generation, and field troubleshooting. DO NOT TRIGGER for creating or customizing the value set itself — defining a new GlobalValueSet, or modifying a StandardValueSet catalog like Industry or Lead Source — use platform-value-set-generate instead; this skill covers the field that references a value set, not the value set definition."
}

Salesforce Custom Field Generator and Validator

Overview

Generates and validates Salesforce CustomField metadata XML, with special handling for the highest-failure-rate types — Roll-Up Summary and Master-Detail. The agent must verify the constraints below before outputting XML to prevent Metadata API deployment errors.


1. Universal Mandatory Attributes

Every generated field must include these tags:

Attribute Requirement Notes
<fullName> Required Field name only: derive from <label> — capitalize each word, replace spaces with _, append __c. Must start with a letter. E.g., label Total Contract ValueTotal_Contract_Value__c. ⚠️ This rule is for the FIELD name. Picklist VALUE <fullName> is different — keep it exactly as the user spelled it, spaces and all, no __c (e.g. Closed Won, NOT Closed_Won). See references/advanced-picklists.md (ref §3).
<label> Required The UI name (Title Case)
<description> Always include Explain the business reason why this field exists.
<inlineHelpText> Always include Actionable end-user guidance that adds value beyond the label (e.g., "Enter the value in USD including tax", not "The amount").

<description> and <inlineHelpText> are mandatory outputs even though the Metadata API does not enforce them — omitting them produces low-quality metadata.

File path (SFDX source format): save each field as force-app/main/default/objects/<Object>/fields/<FieldName>__c.field-meta.xml, where <Object> is the object's API name (Account, Opportunity, or a custom Inventory_Item__c). A correct XML at the wrong path is never seen by the Metadata API.

External ID Configuration

Trigger: If the user mentions "integration," "importing data," "external system ID," or "unique key from [System Name]," set <externalId>true</externalId>.

Applicable Types: Text, Number, Email


2. Precision, Scale, and Length Rules

To ensure deployment success, follow these mathematical constraints:

Precision vs. Scale Rules

  • precision is the total digits; scale is the decimal digits
  • Rule: precision ≤ 18 AND scale ≤ precision
  • Calculation: Digits to the left of decimal = precision - scale

The "Fixed 255" Rule

TextArea: always include <length>255</length> exactly — this literal value is required by the Metadata API and omitting it fails deployment, even though the UI exposes no length control. Unlike every other type where length is a value you calculate, TextArea's is a fixed constant.

Visible Lines

Mandatory for Long/Rich text and Multi-select picklists to control UI height.


3. Field Data Types

3.1 Simple Attribute Types

Type <type> Value Required Attributes
Auto Number AutoNumber displayFormat (must include {0}), startingNumber
Checkbox Checkbox Default defaultValue to false
Date Date No precision/length required
Date/Time DateTime No precision/length required
Email Email Built-in format validation
Lookup Relationship Lookup referenceTo, relationshipName, deleteConstraint
Master-Detail Relationship MasterDetail referenceTo, relationshipName, relationshipOrder
Number Number precision, scale
Currency Currency Default precision: 18, scale: 2
Percent Percent Default precision: 5, scale: 2
Phone Phone Standardizes phone number formatting
Picklist Picklist valueSet containing EITHER valueSetDefinition (inline) OR valueSetName (reference); restricted (see "Picklist restricted default" below; advanced cases in §3.4)
Text Text length (Max 255)
Text Area TextArea <length>255</length>
Text (Long) LongTextArea length, visibleLines (default 3)
Text (Rich) Html length, visibleLines (default 25)
Time Time Stores time only (no date)
URL Url Validates for protocol and format

3.2 Computed & Multi-Value Types

Type <type> Value Required Attributes
Formula Result type (e.g., Number) formula, formulaTreatBlanksAs
Roll-Up Summary Summary See Section 5 for complete requirements
Multi-Select Picklist MultiselectPicklist valueSet, visibleLines (default 4)

3.3 Specialized Types

Type <type> Value Required Attributes
Geolocation Location scale, displayLocationInDecimal

Picklist restricted default

Always set <restricted>true</restricted> inside <valueSet> unless the user explicitly says the picklist should accept custom values not in the admin-defined list (e.g. "unrestricted"/"open"). Restricted sets are capped at 1,000 total values (active + inactive). Minimal inline shape:

<valueSet>
  <restricted>true</restricted>
  <valueSetDefinition>
    <sorted>false</sorted>
    <value><fullName>Option_A</fullName><default>false</default><label>Option A</label></value>
  </valueSetDefinition>
</valueSet>

3.4 Advanced Picklists

The inline <valueSetDefinition> above is the simple case. Full rules and worked ✅/❌ examples for everything below are in references/advanced-picklists.md — load it for any non-trivial picklist. Section numbers in parentheses below (e.g. "ref §1") point to that reference file, not to this skill. The hard rules:

  • Value-set reference (ref §1). A <valueSet> holds EITHER <valueSetName> (reference) OR <valueSetDefinition> (inline) — never both. Reference by the bare developer name — Standard set Industry, GlobalValueSet Priority_Levels with NO __gvs and no __c (the __gvs suffix is org-storage display only; the Metadata API uses the bare name). A value-set-backed field is <restricted>true</restricted>. Creating the value set is the platform-value-set-generate skill's job; this one only references it.
  • Value-name fidelity (ref §3). A picklist value's <fullName>/<label> keep the user's exact text including spaces (Closed Won, never Closed_Won). The space→_ + __c rule is for the FIELD name only.
  • Dependent picklists (ref §2). Use the modern API 38.0+ form: <controllingField> + one <valueSettings> (<controllingFieldValue>+<valueName>) per pair; never the legacy <picklist>/<picklistValues>/<controllingFieldValues> tags. Both controlling and dependent fields MUST be <restricted>true</restricted>, even if the request doesn't say so.
  • Enhanced value attributes (ref §3). <value> entries also accept <color> (hex, leading #), <isActive> (false retires a value), and a value-level <description>.
  • Scoping a picklist to a record type (ref §5). Per-record-type value visibility lives on the RecordType (<picklistValues>), not the field. The RecordType file carries its own <fullName> (bare developer name). First decide if the object needs a BusinessProcess: only Opportunity / Lead / Case / Solution require one — they won't deploy without a <businessProcess> (Required field is missing: businessProcess), even when only a custom picklist is filtered. There you emit two coupled files: the businessProcesses/<Name>.businessProcess-meta.xml file AND a matching <businessProcess><Name></businessProcess> inside the <RecordType> (after <active>, before <picklistValues>; the <fullName> in the BP file is bare, never object-qualified). Custom objects (*__c) and all other standard objects (Account, Contact, …) need NO BusinessProcess — emit the RecordType alone; do not invent one. Scope limit: picklist-value visibility per record type only — NOT general record-type authoring (compact layouts, page layouts, branding).

4. Master-Detail Relationship Rules CRITICAL

Master-Detail fields have strict attribute restrictions that differ from Lookup fields. Violating these rules causes deployment failures.

Forbidden Attributes on Master-Detail Fields

NEVER include these attributes on Master-Detail fields:

Forbidden Attribute Why What Happens
<required> Master-Detail is ALWAYS required by design Deployment error
<deleteConstraint> Master-Detail ALWAYS cascades deletes Deployment error
<lookupFilter> Only supported on Lookup fields Deployment error

Master-Detail vs Lookup Comparison

Attribute Master-Detail Lookup
<required> ❌ FORBIDDEN ✅ Optional
<deleteConstraint> ❌ FORBIDDEN (always CASCADE) ✅ Required (SetNull, Restrict, Cascade)
<lookupFilter> ❌ FORBIDDEN ✅ Optional
<relationshipOrder> ✅ Required (0 or 1) ❌ Not applicable
<reparentableMasterDetail> ✅ Optional ❌ Not applicable
<writeRequiresMasterRead> ✅ Optional ❌ Not applicable

INCORRECT — Master-Detail with forbidden attributes:

<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
  <fullName>Account__c</fullName>
  <type>MasterDetail</type>
  <referenceTo>Account</referenceTo>
  <relationshipName>Contacts</relationshipName>
  <relationshipOrder>0</relationshipOrder>
  <required>true</required>                     <!-- WRONG: remove -->
  <deleteConstraint>Cascade</deleteConstraint>  <!-- WRONG: remove -->
  <lookupFilter>...</lookupFilter>              <!-- WRONG: remove entire block -->
</CustomField>

Errors: Master-Detail Relationship Fields Cannot be Optional or Required · Can not specify 'deleteConstraint' for a CustomField of type MasterDetail · Lookup filters are only supported on Lookup Relationship Fields

CORRECT — Master-Detail field:

<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
  <fullName>Account__c</fullName>
  <label>Account</label>
  <description>Links this record to its parent Account</description>
  <type>MasterDetail</type>
  <referenceTo>Account</referenceTo>
  <relationshipLabel>Child Records</relationshipLabel>
  <relationshipName>ChildRecords</relationshipName>
  <relationshipOrder>0</relationshipOrder>
  <reparentableMasterDetail>false</reparentableMasterDetail>
  <writeRequiresMasterRead>false</writeRequiresMasterRead>
  <!-- NO required, deleteConstraint, or lookupFilter -->
</CustomField>

CORRECT — Lookup field (with optional attributes):

<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
  <fullName>Related_Account__c</fullName>
  <label>Related Account</label>
  <description>Optional link to a related Account</description>
  <type>Lookup</type>
  <referenceTo>Account</referenceTo>
  <relationshipLabel>Related Records</relationshipLabel>
  <relationshipName>RelatedRecords</relationshipName>
  <required>false</required>
  <deleteConstraint>SetNull</deleteConstraint>
  <lookupFilter>
    <active>true</active>
    <filterItems>
      <field>Account.Type</field>
      <operation>equals</operation>
      <value>Customer</value>
    </filterItems>
    <isOptional>false</isOptional>
  </lookupFilter>
</CustomField>

Additional Master-Detail Rules

  • Relationship Order: First Master-Detail on object = 0, second = 1
  • Relationship Name: Must be a plural PascalCase string (e.g., Travel_Bookings)
  • Junction Objects: Use two Master-Detail fields for standard many-to-many (enables Roll-ups)
  • Limit: Maximum 2 Master-Detail relationships per object. Use Lookup for additional relationships.

5. Roll-Up Summary Field Rules CRITICAL

Roll-up Summary fields have the highest deployment failure rate. Follow these rules exactly.

Required Elements for Roll-Up Summary

Element Requirement Format
<type> Required Always Summary
<summaryOperation> Required count, sum, min, or max
<summaryForeignKey> Required ChildObject__c.MasterDetailField__c
<summarizedField> Conditional Required for sum, min, max. NOT for count

Forbidden Elements on Roll-Up Summary

NEVER include these attributes on Roll-Up Summary fields:

Forbidden Attribute Why
<precision> Summary inherits from summarized field
<scale> Summary inherits from summarized field
<required> Not applicable to Summary fields
<length> Not applicable to Summary fields

Format Rules for summaryForeignKey and summarizedField

CRITICAL: Both summaryForeignKey and summarizedField MUST use the fully qualified format:

ChildObjectAPIName__c.FieldAPIName__c

Decision Logic:

  • summaryForeignKey = ChildObject__c.MasterDetailFieldOnChild__c
  • summarizedField = ChildObject__c.FieldToSummarize__c

INCORRECT — Roll-Up Summary with common errors:

<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
  <fullName>Total_Amount__c</fullName>
  <label>Total Amount</label>
  <type>Summary</type>
  <precision>18</precision>           <!-- WRONG: Remove - inherited from source -->
  <scale>2</scale>                    <!-- WRONG: Remove - inherited from source -->
  <summaryOperation>sum</summaryOperation>
  <summaryForeignKey>Order__c</summaryForeignKey>        <!-- WRONG: Missing field name -->
  <summarizedField>Amount__c</summarizedField>           <!-- WRONG: Missing object name -->
</CustomField>

Errors:

  • Can not specify 'precision' for a CustomField of type Summary
  • Must specify the name in the CustomObject.CustomField format (e.g. Account.MyNewCustomField)

CORRECT — Roll-Up Summary (SUM operation):

<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
  <fullName>Total_Amount__c</fullName>
  <label>Total Amount</label>
  <description>Sum of all line item amounts</description>
  <inlineHelpText>Automatically calculated from child line items</inlineHelpText>
  <type>Summary</type>
  <summaryOperation>sum</summaryOperation>
  <summarizedField>Order_Line_Item__c.Amount__c</summarizedField>
  <summaryForeignKey>Order_Line_Item__c.Order__c</summaryForeignKey>
  <!-- NO precision, scale, required, or length -->
</CustomField>

COUNT: identical structure to SUM but omit <summarizedField> entirely (and keep <summaryForeignKey>). MIN / MAX: identical to SUM — just <summaryOperation>min</summaryOperation> or max, with <summarizedField> pointing at the field to find the minimum/maximum of. The Quick Reference table below covers all four.

Roll-Up Summary Quick Reference

Operation summarizedField Required? Use Case
count NO Count number of child records
sum YES Add up numeric values
min YES Find smallest value
max YES Find largest value

Roll-Up Summary Prerequisites

  • Roll-Up Summary fields can ONLY be created on the parent object in a Master-Detail relationship
  • The child object MUST have a Master-Detail field pointing to this parent
  • The summarized field must exist on the child object

6. Formula Field Rules

Formula Result Types

A Formula is not a type itself. The <formula> tag is added to a field whose <type> is set to the result data type:

  • Checkbox, Currency, Date, DateTime, Number, Percent, Text

Formula XML Generation Rules

  • The contents of the <formula> tag MUST be wrapped in a <![CDATA[ ... ]]> section. This prevents the XML parser from interpreting formula operators (like &, <, >) as XML markup.
  • If the formula text itself contains the literal sequence ]]>, escape it by breaking the CDATA block: e.g., <![CDATA[Text_Field__c & "]]]]><![CDATA[>"]]>
  • NEVER use an attribute or tag named returnType. This does not exist in the Metadata API. The <type> tag defines the return data type of the formula result.

formulaTreatBlanksAs Rule

Decision Logic:

  • IF formula result type = Number, Currency, or Percent → set <formulaTreatBlanksAs>BlankAsZero</formulaTreatBlanksAs>
  • IF formula result type = Text, Date, or DateTime → set <formulaTreatBlanksAs>BlankAsBlank</formulaTreatBlanksAs>

INCORRECT — Using Formula as type:

<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
  <fullName>Calculated_Value__c</fullName>
  <type>Formula</type>  <!-- WRONG: Formula is not a valid type -->
  <returnType>Number</returnType>  <!-- WRONG: returnType does not exist in Metadata API -->
  <formula>Field1__c + Field2__c</formula>  <!-- WRONG: Missing CDATA wrapper -->
</CustomField>

CORRECT — Formula field:

<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
  <fullName>Calculated_Value__c</fullName>
  <label>Calculated Value</label>
  <description>Sum of Field1 and Field2</description>
  <type>Number</type>  <!-- Result type, not "Formula" -->
  <precision>18</precision>
  <scale>2</scale>
  <formula><![CDATA[Field1__c + Field2__c]]></formula>
  <formulaTreatBlanksAs>BlankAsZero</formulaTreatBlanksAs>
</CustomField>

Formula Field Dependencies & Functions

  • Formula fields that reference other fields fail deployment if the referenced field doesn't exist or hasn't deployed yet — deploy referenced fields first.
  • Use ISPICKVAL() (not ==) for picklist comparisons.
  • For the full formula-function reference (TEXT/VALUE/CASE/DAY/MONTH/DATEVALUE/ISCHANGED type rules), defer to the platform-validation-rule-generate skill, which owns formula-function correctness.

7. Common Deployment Errors

Error Message Cause Fix
ConversionError: Invalid XML tags or unable to find matching parent xml file for CustomField XML comments placed before the root <CustomField> element Remove XML comments (<!-- ... -->) that appear before <CustomField> in the .field-meta.xml file
Field [FieldName] does not exist. Check spelling. Referenced field does not exist or has not been deployed yet Verify the referenced field exists and is deployed before this field
DUPLICATE_DEVELOPER_NAME Field fullName already exists on the object Use a unique business-driven name
MAX_RELATIONSHIPS_EXCEEDED More than 2 Master-Detail or 15 Lookup fields on the object Use Lookup for 3rd+ Master-Detail; review Lookup count
Reserved keyword error Using Order__c, Group__c, etc. Rename to Status_Order__c, etc.
Value set must reference a value set name or define a value set, but not both <valueSet> has both <valueSetName> and <valueSetDefinition> Keep exactly one (see Section 3.4)
duplicate value found: [X] is defined multiple times Two <value> entries share a <fullName> Make every picklist value <fullName> unique
Invalid fullName on a picklist value Value <fullName> starts with a digit or contains hyphens Start with a letter; no hyphens, no leading digit. Spaces ARE allowed — do NOT underscore them (see §3.4 value-name fidelity)
Element ...picklist is not allowed Deprecated ≤37.0 dependent-picklist syntax (<picklist>/<picklistValues>/<controllingFieldValues>) Use the modern valueSettings/controllingFieldValue/valueName form (Section 3.4)

8. Verification Checklist

Before generating CustomField XML, verify:

Universal Checks

  • Does <fullName> use valid format and end in __c?
  • Are <description> and <inlineHelpText> both populated and meaningful?
  • Is <label> in Title Case?
  • Are there no XML comments (<!-- ... -->) before the root <CustomField> element? (Comments before the root element break SDR's parser)

Master-Detail Field Checks CRITICAL

  • Is <required> attribute ABSENT? (Master-Detail is always required)
  • Is <deleteConstraint> attribute ABSENT? (Master-Detail always cascades)
  • Is <lookupFilter> block ABSENT? (Only for Lookup fields)
  • Is <relationshipOrder> set to 0 or 1?
  • Is parent object's <sharingModel> set to ControlledByParent?

Lookup Field Checks

  • Is <deleteConstraint> set to SetNull, Restrict, or Cascade?
  • Is <relationshipName> in plural PascalCase?

Picklist Field Checks

  • Does each <valueSet> contain EITHER <valueSetName> OR <valueSetDefinition> — never both?
  • For a value-set reference: is <restricted>true</restricted> set?
  • For a StandardValueSet reference: is the name the bare enum with NO __c (e.g. Industry)?
  • For a GlobalValueSet reference: is the name the bare developer name with NO __gvs suffix?
  • For dependent picklists: is <controllingField> set, with one <valueSettings> (<controllingFieldValue> + <valueName>) per pair?
  • For dependent picklists: is the deprecated <picklist>/<picklistValues>/<controllingFieldValues> form ABSENT?
  • Are all picklist value <fullName> values unique, start with a letter, and free of hyphens? (spaces are allowed — do NOT replace them with underscores, per the §3.4 value-name fidelity rule)

Roll-Up Summary Field Checks CRITICAL

  • Is <precision> attribute ABSENT?
  • Is <scale> attribute ABSENT?
  • Is <summaryForeignKey> in format ChildObject__c.MasterDetailField__c?
  • For SUM/MIN/MAX: Is <summarizedField> in format ChildObject__c.FieldName__c?
  • For COUNT: Is <summarizedField> ABSENT?
  • Does the child object have a Master-Detail field to this parent?

Formula Field Checks

  • Is <type> set to result type (NOT "Formula")?
  • Is <formula> content wrapped in <![CDATA[ ... ]]>?
  • Is <returnType> attribute ABSENT? (does not exist in Metadata API)
  • Is <formulaTreatBlanksAs> set to BlankAsZero for numeric results or BlankAsBlank for text/date results?
  • Do all referenced fields exist and deploy before this field?

Numeric Field Checks

  • Is scale ≤ precision?
  • Is precision ≤ 18?

Text Area Checks

  • For TextArea: Is <length>255</length> explicitly included?
  • For LongTextArea/Html: Is <visibleLines> set?

Relationship Limit Checks

  • Are there 2 or fewer Master-Detail relationships on the object?
  • Are there 15 or fewer Lookup relationships on the object?

Naming Checks

  • Is the API name free of reserved words (Order, Group, Select, etc.)?
  • Is the API name unique on this object?
用于创建自定义闪电类型(CLT),支持Einstein Agent结构化输入输出、JSON Schema定义及UI渲染配置。处理嵌套对象、Apex类引用及列表限制,严格遵循元模式规则以避免部署错误。
创建自定义闪电类型 Einstein Agent动作配置 生成JSON Schema类型定义 设置编辑器或渲染器配置 解决CLT部署错误
skills/platform-custom-lightning-type-generate/SKILL.md
npx skills add forcedotcom/sf-skills --skill platform-custom-lightning-type-generate -g -y
SKILL.md
Frontmatter
{
    "name": "platform-custom-lightning-type-generate",
    "metadata": {
        "version": "1.0"
    },
    "description": "Use this skill when users need to create Custom Lightning Types (CLTs) for Einstein Agent actions or structured input\/output schemas. Trigger when users mention CLT, Custom Lightning Types, Custom Lightning Types (CLTs) with widget\/mosaic\/fragment rendition\/renderer, JSON schemas for agents, type definitions, lightning__objectType, or editor\/renderer configurations. When widget renditions are requested, you MUST first read the widget-rendition.md reference file in this skill's references\/ directory and follow its complete workflow. This is complex - always use this skill for CLT work."
}

When to Use This Skill

Use this skill when you need to:

  • Create Custom Lightning Types (CLTs) for structured inputs/outputs
  • Generate JSON Schema-based type definitions for Lightning Platform
  • Configure CLTs for Einstein Agent actions
  • Set up editor and renderer configurations for custom UI
  • Create CLTs with widget/mosaic/fragment rendition
  • Troubleshoot deployment errors related to Custom Lightning Types

Specification

CustomLightningType Metadata Specification

Overview & Purpose

Custom Lightning Types (CLTs) are JSON Schema-based type definitions used by the Lightning Platform (including Einstein Agent actions) to describe structured inputs/outputs and drive editor/renderer experiences.

Configuration

  • Choose referenced CLT pattern for nested objects - When you need a reusable or separately deployed nested type, create a CLT for that shape and reference it with "lightning:type": "c__<CLTName>". That string is the referenced type’s lightning:type value / FQN / registered identifier — not the JSON Schema title.
  • Choose standard Lightning types when the structure is simple and can be expressed with properties and supported primitive lightning:type identifiers.
  • Choose Apex class types (@apexClassType/...) when the structure already exists server-side and you want the Apex class to define the shape.
  • Include editor/renderer config only when you need custom UI behavior (custom LWC input/output components). Otherwise, omit.

Critical Rules (Read First)

  • CRITICAL: NEVER include the "$schema" field in schema.json
    • Salesforce CLT validator WILL REJECT schemas with this field, even if it's a valid JSON Schema $schema declaration.
  • Root object schemas MUST include:
    • "type": "object"
    • "title"
    • "lightning:type": "lightning__objectType"
    • "unevaluatedProperties": false
  • "unevaluatedProperties" is enforced as false by the CLT metaschema. Do not set it to true.
  • Root object schemas MUST NOT include "examples" when "unevaluatedProperties": false is set.
  • Nested objects (inside properties) MUST NOT set "lightning:type": "lightning__objectType".
    • Nested objects can be: references to other CLTs using c__<CLTName> syntax.
  • List/array properties are highly restricted by the CLT metaschema:
    • CRITICAL LIMITATION: the CLT metaschema may reject the items keyword entirely. Treat items as disallowed by default.
    • Root-level arrays (direct children of the root properties):
      • MUST include "lightning:type": "lightning__listType"
      • MUST NOT include "items"
      • OPTIONAL "type": "array"
    • Nested arrays (arrays inside nested objects) are the most common failure:
      • MUST include "type": "array"
      • MUST NOT include "lightning:type": "lightning__listType"
      • MUST NOT include "items"
  • When "unevaluatedProperties": false is set, any unknown keyword will fail validation. Prefer removing keywords over relaxing strictness.
  • Apex class CLTs are minimal:
    • Include only title, description (optional), and lightning:type set to @apexClassType/....
    • Do not add type, properties, required, or unevaluatedProperties.

Additional CLT Metaschema Validations

  • Org namespace validation: titles/descriptions and other string fields may be validated to ensure you are not using an org namespace in places that are disallowed.
  • Lightning type validation: CLTs are validated to prevent referencing internal namespaces (for example, disallowing types from internal namespaces like sfdc_cms where not permitted).
  • Object type validation: the CLT root is validated to ensure lightning:type is exactly lightning__objectType.

Primitive Types & Constraints

When you need the full list of supported primitive lightning:type identifiers, their constraints, and the allowed property-level keywords, read assets/primitive-types-and-constraints.md in this skill's directory.

Generation Workflow

  1. Confirm the CLT approach
    • If referencing Apex: capture the exact class reference (@apexClassType/namespace__ClassName$InnerClass).
    • If using standard primitives: list the fields, their Lightning primitive types, and which fields are required.
  2. Draft schema.json
    • DO NOT include "$schema" at the top
    • Start with the root object structure (required root fields).
    • Add properties using valid primitive lightning:type identifiers.
    • For nested-object properties, use CLT Reference pattern:
      • "lightning:type": "c__<CLTName>" to reference another CLT
      • The referenced CLT must be deployed to the org before the parent CLT.
    • For Apex-based nested objects: Use @apexClassType/... when structure exists server-side.
    • If the prompt explicitly requires true nested object output, prefer an Apex-based CLT (@apexClassType/...) for deploy-safe nested structures.
    • For arrays: follow the strict list rules (avoid items; avoid lightning:type on nested arrays).
    • Before deployment, verify exact lightning:type spellings (for example, use lightning__richTextType, not misspelled variants).
  3. (Optional) Draft editor.json (only if custom UI is required)
    • Supported shape: Top-level editor object with editor.componentOverrides and editor.layout.
      • Top-level editor object.
      • Use editor.componentOverrides for component overrides.
      • Use editor.layout for layout.
      • DEPRECATED: Do NOT use propertyRenderers or view — these are legacy keys. Always use componentOverrides and layout instead.
    • Root override pattern (most common for fully custom editing UI):
      • editor.componentOverrides["$"] = { "definition": "c/<yourEditorComponent>", "attributes": { ... } }
      • When passing schema data into a custom LWC, use attribute mapping with the {!$attrs.<name>} syntax: e.g. "attributes": { "myField": "{!$attrs.value}" } so the runtime binds schema values to your component's attributes.
      • CRITICAL: The <name> in {!$attrs.<name>} must be a property defined in your type schema. For example, if your schema has a property called temperature, use {!$attrs.temperature}, not {!$attrs.value} unless value is an actual property.
    • Property-level override pattern (for individual fields):
      • editor.componentOverrides["<propertyName>"] = { "definition": "es_property_editors/<...>" }
      • Valid editor components (examples): es_property_editors/inputText, es_property_editors/inputNumber, es_property_editors/inputRichText, es_property_editors/inputImage, es_property_editors/inputTextarea. Do not use es_property_editors/inputList.
    • Collection editor (for root-level lightning__listType properties): Use a collection-level override so the list is edited by a custom component: collection.editor.componentOverrides["$"] = { "definition": "c/<yourCollectionEditorComponent>" }. Alternatively, use editor.layout with lightning/propertyLayout and attributes.property = "<listPropertyName>" for default list editing.
    • Layout pattern:
      • editor.layout.definition = "lightning/verticalLayout"
      • editor.layout.children[*].definition = "lightning/propertyLayout" with attributes.property = "<propertyName>"
      • CRITICAL: lightning/propertyLayout only accepts the property attribute. Do NOT add label, title, or any other attributes — these will fail validation with additionalProperties: false errors.
    • Avoid known-invalid patterns:
      • Do not use es_property_editors/inputList.
      • Do not use itemSchema attributes.
  4. (Optional) Draft renderer.json (only if custom UI or mosaic rendition is required)
    • Supported shape: Top-level renderer object with renderer.componentOverrides and renderer.layout.
      • Top-level renderer object.
      • Use renderer.componentOverrides for component overrides.
      • Use renderer.layout for layout.
      • DEPRECATED: Do NOT use propertyRenderers or view — these are legacy keys. Always use componentOverrides and layout instead.
    • Root override pattern (most common for fully custom rendering UI):
      • renderer.componentOverrides["$"] = { "definition": "c/<yourRendererComponent>", "attributes": { ... } }
      • Use {!$attrs.<name>} in attribute mappings when binding schema data to custom renderer component attributes.
      • CRITICAL: Attribute mappings like {!$attrs.propertyName} must reference properties that actually exist in your type schema. Referencing non-existent properties will fail validation.
      • Type matching: Attribute values must match the expected type for the component. For example, if a component expects a string attribute, passing an integer will fail validation.
    • Widget renderer pattern (for widget rendition):
      • When to use: Use this when users request "mosaic", "widget", "fragment", or "cross-platform rendering" for their CLT.
      • Structure: renderer.componentOverrides["$"] = { "type": "mosaic", "definition": "tile/mosaic", "children": [ /* UEM tree of blocks and regions */ ] }
      • REQUIRED workflow:
        • STOP: Do NOT attempt to create the widget renderer yourself.
        • MANDATORY FIRST STEP: You MUST fetch the reference file references/widget-rendition.md located in this skill's directory before proceeding.
        • Follow the complete workflow documented in widget-rendition.md using the generated CLT schema as the grounding schema.
        • The widget-rendition.md reference contains the full widget generation workflow: discovering UEM blocks via discoverUiComponents, calling getUiComponentSchemas, building the UEM tree, and writing renderer.json.
        • Do not attempt to generate widget rendition without first fetching the widget-rendition.md reference file.
    • Property-level override pattern:
      • renderer.componentOverrides["<propertyName>"] = { "definition": "es_property_editors/outputText" | "es_property_editors/outputNumber" | "es_property_editors/outputImage" | ... }. Valid renderer components (examples): es_property_editors/outputText, es_property_editors/outputNumber, es_property_editors/outputImage. Avoid input-style components in the renderer.
    • Layout pattern for renderer:
      • renderer.layout.definition = "lightning/verticalLayout"
      • renderer.layout.children[*].definition = "lightning/propertyLayout" with attributes.property = "<propertyName>"
      • CRITICAL: Same as editor layouts, lightning/propertyLayout only accepts the property attribute. Do NOT add label, title, or any other attributes.
    • Collection renderer (for root-level lightning__listType properties): Use collection.renderer.componentOverrides["$"] = { "definition": "c/<yourListRendererComponent>" } or es_property_editors/genericListTypeRenderer to render the list.
  5. Place files in the correct bundle structure
    • lightningTypes/<TypeName>/schema.json
    • (Optional) lightningTypes/<TypeName>/lightningDesktopGenAi/editor.json
    • (Optional) lightningTypes/<TypeName>/lightningDesktopGenAi/renderer.json
    • For Gen AI / Copilot the standard path is lightningDesktopGenAi/. Other targets (e.g. Experience Builder, Mobile Copilot, Enhanced Web Chat) use different subfolders when supported: experienceBuilder/, lightningMobileGenAi/, enhancedWebChat/.
  6. Configure custom LWC components (if using custom components)
    • CRITICAL: Custom LWC components referenced in editor/renderer configs MUST have the correct target configuration in their -meta.xml files:
      • For editor components (c/<componentName> used in editor.json): The LWC's -meta.xml file must include <target>lightning__AgentforceInput</target>
      • For renderer components (c/<componentName> used in renderer.json): The LWC's -meta.xml file must include <target>lightning__AgentforceOutput</target>
    • Without the correct target, deployment will fail with: Invalid target configuration. To use 'c/componentName' as a renderer/editor, your js-meta.xml file must include valid target 'lightning__AgentforceOutput/Input'.
    • Example -meta.xml for a renderer component:
      <?xml version="1.0" encoding="UTF-8"?>
      <LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
          <apiVersion>60.0</apiVersion>
          <isExposed>true</isExposed>
          <targets>
              <target>lightning__AgentforceOutput</target>
          </targets>
      </LightningComponentBundle>
      

Common Deployment Errors

Error / Symptom Likely Cause Fix
Schema validation fails due to unknown keyword unevaluatedProperties: false + disallowed keyword (commonly examples, items) Remove the offending keyword; keep schema minimal
Nested object validation failure Org/channel validation rejects nested object typing in LightningTypeBundle Use CLT reference (c__<CLTName>) or Apex class types
Invalid CLT reference Referenced CLT doesn't exist in org or incorrect syntax Deploy the referenced CLT first; c__<CLTName> must match the referenced type’s lightning:type value / FQN / registered identifier, not title
Invalid or misspelled lightning:type (for example, lightning__richtextType instead of lightning__richTextType) Incorrect generated type name Cross-check all lightning:type values against supported type names and correct them before deployment
Array property rejected Use of items (or lightning:type in nested arrays) rejected by validator For nested arrays: keep only type: "array". For root arrays: use minimal structure; remove items if rejected
Apex-based CLT rejected Extra fields added (e.g., type, properties) Use only title, optional description, and lightning:type
Editor config rejected Use of invalid patterns (es_property_editors/inputList, itemSchema) or unrecognized top-level keys Use editor.componentOverrides and editor.layout; keep config minimal
additionalProperties error on layout attributes Adding label or other attributes to lightning/propertyLayout Only use property attribute in lightning/propertyLayout. Remove label, title, or any other attributes
Invalid target configuration for custom LWC Custom LWC component's -meta.xml missing required target (lightning__AgentforceInput or lightning__AgentforceOutput) Add correct target to LWC's -meta.xml: use lightning__AgentforceInput for editors, lightning__AgentforceOutput for renderers
Attribute mapping doesn't exist in type schema Using {!$attrs.propertyName} where propertyName is not defined in schema Ensure all attribute mappings reference actual properties in your type schema's properties section
additionalProperties error with deprecated keys Using propertyRenderers or view in editor/renderer config Replace deprecated propertyRenderers with componentOverrides and view with layout
Type mismatch in component attributes Passing wrong type for component attribute (e.g., integer instead of string) Ensure attribute values match the expected type defined by the component

Verification Checklist

  • Root schema has type: "object", title, lightning:type: "lightning__objectType", and unevaluatedProperties: false
  • Root schema does not include examples when strict validation is enabled
  • No nested object includes lightning:type: "lightning__objectType"
  • Arrays are defined minimally (especially nested arrays)
  • Only supported primitive lightning:type identifiers are used for leaf properties
  • Apex class CLTs contain only title/description and lightning:type: "@apexClassType/..."
  • Bundle structure and filenames match Lightning Types requirements
  • Editor config uses only allowed patterns (no es_property_editors/inputList, no itemSchema); use valid components (e.g. es_property_editors/inputText, es_property_editors/inputNumber) or custom c/ components
  • Renderer config uses output-style components (e.g. es_property_editors/outputText, es_property_editors/outputNumber) where applicable, not input editors
  • Layout configurations use lightning/propertyLayout with ONLY the property attribute (no label, title, or other attributes)
  • All attribute mappings ({!$attrs.propertyName}) reference properties that exist in the type schema
  • Custom LWC components have correct targets in -meta.xml: lightning__AgentforceInput for editors, lightning__AgentforceOutput for renderers
  • Root schema does NOT include "$schema" field
用于创建、生成或验证Salesforce自定义对象元数据。涵盖对象定义、字段/校验规则变更及描述更新,处理分享模型与主从关系配置,排除非自定义对象元数据工作。
创建自定义对象 生成对象元数据XML 配置对象分享和安全设置 添加、更新或删除字段或校验规则 排查自定义对象部署错误 涉及主从关系的对象配置
skills/platform-custom-object-generate/SKILL.md
npx skills add forcedotcom/sf-skills --skill platform-custom-object-generate -g -y
SKILL.md
Frontmatter
{
    "name": "platform-custom-object-generate",
    "metadata": {
        "version": "1.1",
        "minApiVersion": "60.0"
    },
    "description": "Use this skill when users need to create, generate, or validate Salesforce Custom Object metadata. Trigger when users mention custom objects, creating objects, object metadata, .object files, sharing models, name fields, or validation rules on objects. Also use when users say things like \"create a custom object\", \"generate object metadata\", \"set up an object for...\", or when they're troubleshooting object deployment errors especially around sharing models and Master-Detail relationships. Always use this skill for any custom object metadata work, including enriching and keeping the object's description current whenever its fields or validation rules change. Do NOT use this skill for non-Custom-Object metadata (Apex, Flows, LWC, Permission Sets, Custom Metadata Types) or for standard Salesforce objects."
}

When to Use This Skill

Use this skill when you need to:

  • Create new custom objects
  • Generate custom object metadata XML
  • Configure object sharing and security settings
  • Set up object features and capabilities
  • Troubleshoot deployment errors related to custom objects
  • Add, update, or delete a field OR a validation rule on an existing object — any of these may make the object's <description> stale, so you must refresh it (propose + confirm). This applies equally to validation-rule changes, not just fields. See Section 3.B.

Specification

1. Overview and Purpose

This document defines the mandatory constraints for generating CustomObject metadata XML (.object-meta.xml file). The agent must verify these constraints before outputting XML to prevent Metadata API deployment errors.

File extension: .object-meta.xml

🔔 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.


2. Syntactic Essentials (Tier 1)

The following constraints must be true for the XML body to deploy successfully.

Note: The API Name (fullName) is NOT a tag; it is the filename (e.g., Vehicle__c.object-meta.xml).

Required Elements

Element Requirement Notes
<label> Required Singular UI name
<pluralLabel> Required Plural UI name
<sharingModel> Required See Sharing Model Rules below
<deploymentStatus> Required Always set to Deployed
<nameField> Required Primary record identifier (requires <label> and <type>)
<visibility> Required Always set to Public

Sharing Model Rules

Default: Set <sharingModel> to ReadWrite.

Exception: If this object contains a Master-Detail relationship field, <sharingModel> MUST be ControlledByParent.

Decision Logic:

  • IF object has NO Master-Detail field → use ReadWrite
  • IF object has Master-Detail field → use ControlledByParent
  • IF a Master-Detail field is being added to an existing child object → that existing object's <sharingModel> must also be updated to ControlledByParent

❌ INCORRECT — Will cause error: Cannot set sharingModel to ReadWrite on a CustomObject with a MasterDetail relationship field

<CustomObject xmlns="http://soap.sforce.com/2006/04/metadata">
  <label>Order Line Item</label>
  <pluralLabel>Order Line Items</pluralLabel>
  <sharingModel>ReadWrite</sharingModel>  <!-- WRONG: Object has a M-D field -->
  <deploymentStatus>Deployed</deploymentStatus>
</CustomObject>

✅ CORRECT:

<CustomObject xmlns="http://soap.sforce.com/2006/04/metadata">
  <label>Order Line Item</label>
  <pluralLabel>Order Line Items</pluralLabel>
  <sharingModel>ControlledByParent</sharingModel>  <!-- CORRECT -->
  <deploymentStatus>Deployed</deploymentStatus>
</CustomObject>

3. Smart Defaults & Decision Logic (Tier 2)

The agent must choose which features to enable based on the object's intended use case.

A. The Name Field Decision

Type When to Use Additional Requirements
Text Default for human-named entities (Projects, Locations, Teams) None
AutoNumber Use for transactions, logs, or IDs (Invoices, Requests, Tickets) Must include <displayFormat> (e.g., INV-{0000}) and <startingNumber>1</startingNumber>

Text Name Field Example:

<nameField>
  <label>Project Name</label>
  <type>Text</type>
</nameField>

AutoNumber Name Field Example:

<nameField>
  <label>Invoice Number</label>
  <type>AutoNumber</type>
  <displayFormat>INV-{0000}</displayFormat>
  <startingNumber>1</startingNumber>
</nameField>

B. Object Description (Enrichment)

<description>: Mandatory — every Custom Object MUST have one. It must read like human-written documentation, never a generic template ("Object used to track and manage...") or a metadata dump ("Contains 8 fields including Project_Name__c...").

Always compose an enriched description — when creating the object, and again on any change to it: adding, updating, or deleting a field or a validation rule (so it never goes stale). The change — field or validation rule — is never "done" until you've refreshed the object's description. This is not optional; do not ask whether to add a description.

Confirm per change — every time. Propose and confirm on each field/rule change separately. A previous "keep current" applies only to that one change; it is never standing permission to skip the proposal on a later change. Do not infer a preference from an earlier answer — re-propose and re-ask for every new change.

Compose the description (steps below). If the object already has one, use it as a strong signal — preserve the business context it carries (domain, team, intent the schema can't reveal) and fold the new field/rule in rather than discarding it.

Then branch on whether a description already exists:

  • No existing description (brand-new object): there is nothing to overwrite — just write the composed description. Do not prompt.

  • An existing description (update, delete, or any re-enrichment): never overwrite it silently — you can't tell from the file whether it was hand-written by an admin or generated earlier. Show the proposal, ask, and STOP — wait for the user's reply before writing:

    Proposed description for {Object}: <the enriched description> Current: <the existing description> Use this? (yes / keep current / edit)

    You MUST NOT write the <description> until the user replies — showing the diff is not approval, even when the change looks obvious or minor. Then act: yes → write the proposed text · keep current → leave the existing one untouched (this applies to this change only — re-propose on the next one) · edit → use the user's wording.

Always end with a <description> written.

Composing the description:

  1. Classify each field by how it appears in the description:
    • Constrained (required, unique, externalId, restricted picklist) → selective parenthetical: VIN (required, external ID), Color (Red/Green only)
    • Behavioral (formula, roll-up) → describe what it computes: "the Age Years field auto-calculates vehicle age"
    • Relationship (master-detail, lookup) → woven context: "as a child of Account" (never "(Master-Detail to Account)")
    • Standard → label only
  2. Compose in this order, using field labels not API names:

    Purpose → key fields → computed fields → validation rules (as business rules) → "Commonly used for {use cases}."

  3. Count and trim before writing (required): count the words; aim ~45, hard ceiling 50. If over, tighten wording first, then drop whole sentences in priority order (use cases → rules → computed; never drop sentences 1–2). Recount. Do not write until ≤ 50.

Example (Car, 46 words):

<description>The Car object tracks vehicle inventory and maintenance. It captures Year, VIN (required, external ID), Color (Red/Green only), and Location; the Age Years field auto-calculates vehicle age. VIN is required and Black cars cannot be sold. Commonly used for fleet management, inventory tracking, and service scheduling.</description>

→ For the full workflow and examples, read references/description-enrichment.md.

C. Junction Object Naming

If the object is a many-to-many link between two parents, name the object by combining the two parent entities to ensure the schema remains intuitive.

Examples:

  • Position_Candidate__c (links Position and Candidate)
  • Job_Application__c (links Job and Application)

D. Feature Enablement (Clean XML)

To maintain "Clean XML," only include optional tags when deviating from the Salesforce platform default of false.

Scenario A: User-Facing Objects (Apps, Trackers, Business Entities)

  • Trigger: The object is intended for direct user interaction
  • Action: Set <enableSearch>, <enableReports>, <enableActivities>, and <enableHistory> to true

Scenario B: System-Facing Objects (Junctions, Background Logs)

  • Trigger: The object exists for technical associations or background data
  • Action: Omit these tags to keep the UI clean and the XML lean

4. Critical Constraints & Common Failures

Reserved Words

Never use reserved words as API names for Custom Objects or Custom Fields:

Category Reserved Words (Do Not Use as API Names)
SOQL/SQL Select, From, Where, Limit, Order, Group
System User, External, View, Type
Temporal Date, Number

Relationship Cap

Do not create more than 2 Master-Detail relationships for a single object. If a third relationship is required, use a Lookup instead.

XML Root Element

Do NOT include the <fullName> tag at the root of the .object-meta.xml file. The API name is derived from the filename.

❌ INCORRECT:

<CustomObject xmlns="http://soap.sforce.com/2006/04/metadata">
  <fullName>Vehicle__c</fullName>  <!-- WRONG: Remove this -->
  <label>Vehicle</label>
</CustomObject>

✅ CORRECT:

<CustomObject xmlns="http://soap.sforce.com/2006/04/metadata">
  <label>Vehicle</label>
  <!-- fullName comes from filename: Vehicle__c.object-meta.xml -->
</CustomObject>

Validation Rule Naming Convention

Validation rule names follow different conventions than custom fields.

Rules:

  • Must contain only alphanumeric characters and underscores
  • Must begin with a letter
  • Cannot end with an underscore
  • Cannot contain two consecutive underscores
  • Must NOT end with __c (unlike custom fields)

❌ INCORRECT:

<validationRules>
  <fullName>Require_Start_Date__c</fullName>  <!-- WRONG: Has __c suffix -->
  <active>true</active>
  <errorMessage>Start Date is required.</errorMessage>
  <formula>ISBLANK(Start_Date__c)</formula>
</validationRules>

Error: The validation name can only contain alphanumeric characters, must begin with a letter, cannot end with an underscore...

✅ CORRECT:

<validationRules>
  <fullName>Require_Start_Date</fullName>  <!-- CORRECT: No __c suffix -->
  <active>true</active>
  <errorMessage>Start Date is required.</errorMessage>
  <formula>ISBLANK(Start_Date__c)</formula>
</validationRules>

Naming Pattern Reference:

Metadata Type Naming Pattern Example
Custom Fields Ends with __c Start_Date__c
Validation Rules No suffix Require_Start_Date
Custom Objects Ends with __c Vehicle__c

5. Verification Checklist

Before generating the Custom Object XML, verify:

Syntactic Checks

  • Are both <label> and <pluralLabel> present?
  • Is <deploymentStatus> set to Deployed?
  • Is <visibility> set to Public?
  • Does <nameField> include both <label> and <type>?
  • If <type> is AutoNumber, are <displayFormat> and <startingNumber> included?

Sharing Model Check (Critical)

  • Does this object have a Master-Detail relationship field?
    • If YES → <sharingModel> MUST be ControlledByParent
    • If NO → <sharingModel> should be ReadWrite

Constraint Checks

  • Is the API name free of reserved words?
  • Are there 2 or fewer Master-Detail relationships?
  • Is <fullName> absent from the XML root?

Validation Rule Checks (if applicable)

  • Do validation rule names NOT end with __c?
  • Do validation rule names follow alphanumeric + underscore pattern?

Description Enrichment Quality Checks

  • Opens with "The {Object} object..." + business purpose (not "Object used to track and manage...")
  • Uses field labels, never API names; no "Contains N fields including" dump
  • Formulas/rollups described by behavior; validations stated as business rules; relationships as context
  • Includes common use cases ("Commonly used for...") and is under 50 words
  • Folded any current description's business context into the proposed one (didn't discard it)
  • For an existing description (update/delete/re-enrich), STOPPED and waited for the user's reply before writing — did not treat showing the diff as approval

Architectural Checks

  • Is <description> present? (Enriched per Section B — proposed and confirmed with the user before writing.)
  • Are <enableSearch> and <enableReports> set to true if user-facing?
  • Does the filename match the intended API name?

Reference File Index

File When to read
references/description-enrichment.md Composing or refreshing an object's <description> (on create, or when a field/rule changes) — full enrichment workflow, field-prioritization tiers, junction/child handling, edge cases, and more examples
负责Salesforce数据的增删改查、批量导入导出、测试数据生成及清理。适用于sf CLI命令、匿名Apex脚本及数据工厂模式。排除纯SOQL查询、Apex测试执行及元数据部署场景。
用户需要创建或修改Salesforce记录 执行批量数据导入或导出操作 生成用于Apex测试的模拟数据 使用sf data CLI进行数据管理 清理或播种Org中的测试记录
skills/platform-data-manage/SKILL.md
npx skills add forcedotcom/sf-skills --skill platform-data-manage -g -y
SKILL.md
Frontmatter
{
    "name": "platform-data-manage",
    "metadata": {
        "version": "1.1"
    },
    "description": "Salesforce data operations with 130-point scoring. Use this skill to create, update, delete, bulk import\/export, generate test data, and clean up org records using sf CLI and anonymous Apex. TRIGGER when: user creates test data, performs bulk import\/export, uses sf data CLI commands, needs data factory patterns for Apex tests, or needs to seed\/clean records in a Salesforce org. DO NOT TRIGGER when: SOQL query writing only (use platform-soql-query), Apex test execution (use platform-apex-test-run), or metadata deployment (use platform-metadata-deploy)."
}

Salesforce Data Operations Expert (platform-data-manage)

Use this skill when the user needs Salesforce data work: record CRUD, bulk import/export, test data generation, cleanup scripts, or data factory patterns for validating Apex, Flow, or integration behavior.

When This Skill Owns the Task

Use platform-data-manage when the work involves:

  • sf data CLI commands
  • record creation, update, delete, upsert, export, or tree import/export
  • realistic test data generation
  • bulk data operations and cleanup
  • Apex anonymous scripts for data seeding / rollback

Delegate elsewhere when the user is:


Important Mode Decision

Confirm which mode the user wants:

Mode Use when
Script generation they want reusable .apex, CSV, or JSON assets without touching an org yet
Remote execution they want records created / changed in a real org now

Do not assume remote execution if the user may only want scripts.


Required Context to Gather First

Ask for or infer:

  • target object(s)
  • org alias, if remote execution is required
  • operation type: query, create, update, delete, upsert, import, export, cleanup
  • expected volume
  • whether this is test data, migration data, or one-off troubleshooting data
  • any parent-child relationships that must exist first

Core Operating Rules

  • platform-data-manage acts on remote org data unless the user explicitly wants local script generation.
  • Objects and fields must already exist before data creation.
  • For automation testing, prefer 251+ records when bulk behavior matters.
  • Plan cleanup before creating large or noisy datasets — untracked records accumulate across runs and pollute org state.
  • Use synthetic, non-identifying data in test records — real PII creates compliance risk and cannot be safely removed after bulk import.
  • Prefer CLI-first for straightforward CRUD; use anonymous Apex when the operation truly needs server-side orchestration.

If metadata is missing, stop and hand off to:


Recommended Workflow

1. Verify prerequisites

Confirm object / field availability, org auth, and required parent records.

2. Run describe-first pre-flight validation when schema is uncertain

Before creating or updating records, use object describe data to validate:

  • required fields
  • createable vs non-createable fields
  • picklist values
  • relationship fields and parent requirements

See references/sf-cli-data-commands.md for the sf sobject describe command and jq filter patterns for inspecting fields, picklist values, and createable constraints.

3. Choose the smallest correct mechanism

Need Default approach
small one-off CRUD sf data single-record commands
large import/export Bulk API 2.0 via sf data ... bulk
parent-child seed set tree import/export
reusable test dataset factory / anonymous Apex script
reversible experiment cleanup script or savepoint-based approach

4. Execute or generate assets

Use the built-in templates under assets/ when they fit:

  • assets/factories/
  • assets/bulk/
  • assets/cleanup/
  • assets/soql/
  • assets/csv/
  • assets/json/

5. Verify results

Check counts, relationships, and record IDs after creation or update.

6. Apply a bounded retry strategy

If creation fails:

  1. try the primary CLI shape once
  2. retry once with corrected parameters
  3. re-run describe / validate assumptions
  4. pivot to a different mechanism or provide a manual workaround

Do not repeat the same failing command indefinitely.

7. Leave cleanup guidance

Provide exact cleanup commands or rollback assets whenever data was created.


High-Signal Rules

Bulk safety

  • use bulk operations for large volumes
  • test automation-sensitive behavior with 251+ records where appropriate
  • avoid one-record-at-a-time patterns for bulk scenarios

Data integrity

  • include required fields
  • validate picklist values before creation
  • verify parent IDs and relationship integrity
  • account for validation rules and duplicate constraints
  • exclude non-createable fields from input payloads

Cleanup discipline

Prefer one of:

  • delete-by-ID
  • delete-by-pattern
  • delete-by-created-date window
  • rollback / savepoint patterns for script-based test runs

Common Failure Patterns

Error Likely cause Default fix direction
INVALID_FIELD wrong field API name or FLS issue verify schema and access
REQUIRED_FIELD_MISSING mandatory field omitted include required values from describe data
INVALID_CROSS_REFERENCE_KEY bad parent ID create / verify parent first
FIELD_CUSTOM_VALIDATION_EXCEPTION validation rule blocked the record use valid test data or adjust setup
invalid picklist value guessed value instead of describe-backed value inspect picklist values first
non-writeable field error field is not createable / updateable remove it from the payload
bulk limits / timeouts wrong tool for the volume switch to bulk / staged import

Output Format

When finishing, report in this order:

  1. Operation performed
  2. Objects and counts
  3. Target org or local artifact path
  4. Record IDs / output files
  5. Verification result
  6. Cleanup instructions

Suggested shape:

Data operation: <create / update / delete / export / seed>
Objects: <object + counts>
Target: <org alias or local path>
Artifacts: <record ids / csv / apex / json files>
Verification: <passed / partial / failed>
Cleanup: <exact delete or rollback guidance>

Cross-Skill Integration

Need Delegate to Reason
create missing custom objects platform-custom-object-generate schema must exist before data operations
create missing custom fields platform-custom-field-generate field-level schema must exist before data creation
run bulk-sensitive Apex validation platform-apex-test-run test execution and coverage
deploy missing schema first platform-metadata-deploy metadata readiness
implement production Apex logic consuming the data platform-apex-generate Apex class / trigger authoring
implement Flow logic consuming the data automation-flow-generate Flow authoring and automation

Reference Map

Start here

Query / bulk / cleanup

Examples / limits

Validation scripts

Asset templates

  • assets/factories/ — Apex test data factory scripts (account, contact, opportunity, lead, user, etc.)
  • assets/bulk/ — Bulk API 2.0 Apex templates (insert 200, 500, 10000 records; upsert by external ID)
  • assets/cleanup/ — Cleanup and rollback scripts (delete by name, date, pattern; transaction rollback)
  • assets/soql/ — SOQL query templates (aggregate, subquery, parent-to-child, child-to-parent, polymorphic)
  • assets/csv/ — CSV import templates for Account, Contact, Opportunity, custom objects
  • assets/json/ — JSON tree import templates (account-contact, account-opportunity, full hierarchy)

Score Guide

Score Meaning
117+ strong production-safe data workflow
104–116 good operation with minor improvements possible
91–103 acceptable but review advised
78–90 partial / risky patterns present
< 78 blocked until corrected
用于从 Salesforce 官方域名检索权威文档,解决 JS 渲染页面抓取难题。适用于 Apex、LWC、Agentforce 等官方参考文档查询,避免使用第三方内容或执行代码变更任务。
用户请求 Salesforce 官方文档 需要获取 Apex 或 API 参考信息 查询 LWC 或 Agentforce 文档 查找设置或帮助文章 涉及 Salesforce 自有域名的文档检索
skills/platform-docs-get/SKILL.md
npx skills add forcedotcom/sf-skills --skill platform-docs-get -g -y
SKILL.md
Frontmatter
{
    "name": "platform-docs-get",
    "metadata": {
        "version": "1.1"
    },
    "description": "Official Salesforce documentation retrieval skill. Use when you need authoritative Salesforce docs from developer.salesforce.com, help.salesforce.com, architect.salesforce.com, admin.salesforce.com, or lightningdesignsystem.com, especially when pages are JS-heavy, shell-rendered, or hard to extract with naive fetching. Use to ground answers in official Salesforce sources instead of third-party blogs or summaries. TRIGGER when: user asks for official Salesforce documentation, Apex or API reference, LWC docs, Agentforce docs, setup or help articles, or any doc from a Salesforce-owned domain. DO NOT TRIGGER when: user is asking for a code change, deployment task, or anything not requiring documentation retrieval — use the appropriate sf-* skill instead."
}

platform-docs-get

Use this skill to retrieve and ground answers in official Salesforce documentation on the public web.

This skill provides a reliable online retrieval playbook for Salesforce docs that are hard to fetch, especially help.salesforce.com, JS-heavy developer.salesforce.com, Lightning Design System docs on lightningdesignsystem.com, and other official Salesforce-owned doc pages such as architect.salesforce.com and admin.salesforce.com.

Optional extraction scripts are available in scripts/ — see the Reference File Index below.

Scope

In scope Official Salesforce doc retrieval: Apex, API, LWC, metadata, Agentforce, setup articles, SLDS, architect/admin guidance
Out of scope Third-party blogs, PDF fallback, local corpus indexing, benchmark workflows, generating code or metadata

Required Inputs

Before fetching, identify:

  • The exact concept, identifier, class, method, or feature name being requested
  • The likely doc family (developer docs, help articles, design system, architect/admin)

No additional setup is required to use the retrieval playbook in this skill. The optional extraction scripts require playwright — see requirements.txt.

Official Sources Only

Prefer Salesforce-owned documentation sources:

  • developer.salesforce.com
  • help.salesforce.com
  • architect.salesforce.com
  • admin.salesforce.com
  • lightningdesignsystem.com
  • other official Salesforce documentation pages when Salesforce uses them as the source of truth

Avoid third-party blogs, videos, or summary articles unless the user explicitly asks for them.

Do not fall back to PDFs.

Retrieval Workflow

1. Classify the request first

Before fetching anything, identify the likely doc family.

Family Typical Source Use For
Developer docs developer.salesforce.com/docs/... Apex, APIs, LWC, metadata, Agentforce developer docs
Help docs help.salesforce.com/... setup, admin, product configuration
Architect/Admin docs architect.salesforce.com/..., admin.salesforce.com/... best practices, patterns, well-architected guidance, admin enablement
Design system docs lightningdesignsystem.com/... SLDS, Cosmos, design tokens, component and styling guidance
Legacy atlas docs developer.salesforce.com/docs/atlas.en-us.* older official guide and reference docs

2. Identify the exact concept

Extract the real target before you search:

  • exact API/class/method name
  • exact feature name
  • exact product phrase
  • exact setup concept

Examples:

  • Lightning Message Service
  • Wire Service
  • System.StubProvider
  • Agentforce Actions
  • Messaging for In-App and Web allowed domains

3. Prefer targeted official retrieval

Do not broad-crawl Salesforce docs.

Instead:

  1. identify the most likely official guide root or article
  2. if search is needed, restrict it to official Salesforce domains only
  3. fetch that official page
  4. check whether the exact concept actually appears on the page
  5. if not, inspect and follow the most relevant 1–3 official child links
  6. stop once you have grounded evidence

4. Do not stop at broad landing pages

A guide landing page is not enough unless it clearly contains the exact requested concept.

This is especially important for:

  • LWC docs
  • Agentforce docs
  • broad platform guide homepages
  • help landing pages that link to the real article

5. For developer.salesforce.com

Use this playbook:

  • start with the most likely official guide root
  • if the page is JS-heavy, prefer browser-rendered extraction
  • check whether the exact concept appears on the page
  • if the concept is missing, inspect official child links and follow the best matching 1–3 links
  • prefer exact concept pages over broad guide roots
  • legacy atlas pages are valid if they are the real official reference for the concept

6. For help.salesforce.com

Help pages often fail with naive fetching.

Use this playbook:

  • prefer exact articleView?id=... URLs when available
  • use browser-rendered extraction when plain fetch returns shell content
  • treat outputs like Loading, Sorry to interrupt, CSS Error, or mostly chrome/navigation text as failed extraction, not evidence
  • look for the real article body, not just header, nav, or footer text
  • reject shell pages and soft-404 pages such as:
    • "We looked high and low but couldn't find that page"
    • generic empty help shells
  • if starting from a nearby guide or hub page, follow linked Help articles until you reach the real article body
  • if extraction still fails after targeted retries, return the best official Help URLs you found and explicitly say that article-body extraction was unsuccessful

Acceptance Rules

A page is good enough to answer from only when at least one of these is true:

  • the exact identifier appears on the page
  • the exact concept phrase appears on the page
  • multiple query-specific phrases appear in the correct official context

A page is not good enough when:

  • it is only a broad landing page
  • it is a shell page with little real article text
  • it is from the wrong product area
  • it does not contain the requested identifier or concept
  • it is a third-party explanation when an official page should exist

Rejection Rules

Reject these as final evidence:

  • broad guide homepages without the exact concept
  • release notes when a concept/reference page is expected
  • admin blog posts when developer docs are requested
  • third-party blogs when official docs are available
  • shell-rendered pages with no real article body
  • pages whose titles sound right but whose body does not contain the requested concept

Grounding Requirements

When answering, include:

  1. guide/article title
  2. exact official URL
  3. source type:
    • developer doc page
    • atlas reference page
    • help article page
  4. any caveat if extraction was partial or browser-rendered

If evidence is weak, say so plainly.

Examples

Example: Lightning Message Service

Do not stop at the general LWC guide root. Find the exact LWC page for Lightning Message Service or follow the most relevant child links from the LWC docs until the exact concept appears.

Example: Wire Service

Do not answer from the LWC homepage unless Wire Service is actually present there. Follow the relevant child doc page for wire service or wire adapters.

Example: Agentforce Actions

Do not answer from a broad Agentforce landing page or a blog post. Find the official Agentforce developer page for actions, or follow the best matching child pages from the official Agentforce docs.

Example: Messaging for In-App and Web allowed domains

Prefer official Help articles and browser-rendered extraction. Reject generic help shells. Follow linked Help articles from nearby official messaging docs if needed.

Example: System.StubProvider

Prefer the official Salesforce reference/developer page where the exact identifier appears. Do not substitute a broader Apex landing page if the identifier is absent.

Non-Goals

This skill should not:

  • maintain a local documentation corpus
  • rely on a local index
  • use PDF fallback
  • run benchmark workflows
  • depend on repo-specific scripts to be useful

Output Expectations

For each retrieval, include:

  1. Guide or article title
  2. Exact official URL
  3. Source type (developer doc page / atlas reference page / help article page)
  4. Any caveat if extraction was partial or browser-rendered

If evidence is weak, say so plainly rather than forcing an answer.


Reference File Index

File When to read
scripts/extract_salesforce_doc.py Use to fetch any official Salesforce doc URL; automatically routes help.salesforce.com into the dedicated Help extractor and supports browser-rendered extraction for all Salesforce-owned doc hosts
scripts/extract_help_salesforce.py Use directly when targeting help.salesforce.com articleView URLs; use when the wrapper is not appropriate
scripts/runtime_bootstrap.py Imported by the extraction scripts to resolve the isolated platform-docs-get Python runtime and Playwright browser path; not called directly
requirements.txt Lists Python dependencies (playwright, playwright-stealth) needed to run the extraction scripts
用于创建、生成、修改或验证Salesforce Lightning页面(FlexiPages)。适用于记录页、应用页、主页及组件配置,指导使用CLI模板生成XML结构以避免部署错误。
用户需要创建或修改Salesforce Lightning页面 提及RecordPage, AppPage, HomePage或FlexiPage XML文件 询问如何添加组件到页面或解决页面部署错误
skills/platform-flexipage-generate/SKILL.md
npx skills add forcedotcom/sf-skills --skill platform-flexipage-generate -g -y
SKILL.md
Frontmatter
{
    "name": "platform-flexipage-generate",
    "metadata": {
        "version": "1.0"
    },
    "description": "Use this skill when users need to create, generate, modify, or validate Salesforce Lightning pages (FlexiPages). Trigger when users mention RecordPage, AppPage, HomePage, Lightning pages, page layouts, adding components to pages, or page customization. Also use when users say things like 'create a Lightning page', 'add a component to a page', 'customize the record page', 'generate a FlexiPage', or when they're working with FlexiPage XML files and need help with components, regions, or deployment errors. Always use this skill for any FlexiPage-related work, even if they just mention 'page' in the context of Salesforce."
}

When to Use This Skill

Use this skill when you need to:

  • Create Lightning pages (RecordPage, AppPage, HomePage)
  • Generate FlexiPage metadata XML
  • Add components to existing FlexiPages
  • Troubleshoot FlexiPage deployment errors
  • Understand FlexiPage structure and component configuration
  • Work with page layouts or Lightning page customization
  • Edit or update ANY *.flexipage-meta.xml file

Specification

FlexiPage Generation Guide

Overview

CRITICAL: When creating NEW FlexiPages, you MUST ALWAYS start with the CLI template command. Never create FlexiPage XML from scratch - the CLI provides valid structure, proper regions, and correct component configuration that prevents deployment errors.

Generate Lightning pages (RecordPage, AppPage, HomePage) using CLI bootstrapping for component discovery and configuration.


Quick Start Workflow

Step 1: Bootstrap with CLI

MANDATORY FOR NEW PAGES: This step is NOT optional. Always use the CLI template command when creating a new FlexiPage. The CLI generates valid XML structure, proper regions, and correct metadata that prevents common deployment errors. Only skip this step if you're editing an existing FlexiPage file.

sf template generate flexipage \
  --name <PageName> \
  --template <RecordPage|AppPage|HomePage> \
  --sobject <SObject> \
  --primary-field <Field1> \
  --secondary-fields <Field2,Field3> \
  --detail-fields <Field4,Field5,Field6,Field7> \
  --output-dir force-app/main/default/flexipages

CRITICAL: If the sf template generate flexipage command fails, STOP.

  1. Install the templates plugin:
    sf plugins install templates
    
  2. Retry the sf template generate flexipage command
  3. Verify the FlexiPage XML file was created

Do NOT continue to Step 2 until the template command succeeds. The generated XML is required for the entire workflow.

Template-specific requirements

RecordPage:

  • Requires --sobject (e.g., Account, Custom_Object__c)
  • Requires field parameters:
    • --primary-field: Most important identifying field (e.g., Name)
    • --secondary-fields: Record summary (recommended 4-6, max 12)
    • --detail-fields: Full record details, including required fields (e.g., Name)

AppPage:

  • No additional requirements

HomePage:

  • No additional requirements

Field Selection Rules

  • Validate fields exist: Use MCP tools or describe commands to discover available fields for the object before specifying them in the command
  • Prefer compound fields: Use Name (not FirstName/LastName), BillingAddress (not BillingStreet/BillingCity/BillingState), MailingAddress, etc. when available
  • Include required fields in detail-fields: Always include object required fields (like Name) in the --detail-fields parameter, even if they're also used in --primary-field or --secondary-fields

What you get

  • Valid FlexiPage XML with correct structure
  • Pre-configured regions and basic components
  • Proper field references and facet structure
  • Ready to deploy as-is or enhance further

Step 2: Deploy Base Page

Run a dry-run deployment of the entire project to validate the page and dependencies:

sf project deploy start --dry-run -d "force-app/main/default" --test-level NoTestRun --wait 10 --json

Critical: Fix any deployment errors before proceeding. The page must validate successfully.

Step 3: STOP - No Further Modifications

MANDATORY: Stop after Step 2. Do not add components or edit the FlexiPage XML.

This applies even if the user requested:

  • Additional components
  • Page customization
  • Component configuration

What you CAN do:

  • Suggest what components would be useful
  • Explain what enhancements are possible
  • Document what would need to be added manually

What you CANNOT do:

  • Modify the XML file
  • Add any components
  • Make any enhancements

Critical XML Rules

1. Property Value Encoding (MOST COMMON ERROR)

Any property value with HTML/XML characters MUST be manually encoded in the following order (wrong order causes double-encoding corruption):

1. & → &amp;   (FIRST! Encode this before others)
2. < → &lt;
3. > → &gt;
4. " → &quot;
5. ' → &apos;

Wrong:

<value><b>Important</b> text</value>

Correct:

<value>&lt;b&gt;Important&lt;/b&gt; text</value>

Check your XML: Search for <value> tags - they should never contain raw < or > characters.

2. Field References

ALWAYS: Record.{FieldApiName}
NEVER: {ObjectName}.{FieldApiName}

<!-- Correct -->
<fieldItem>Record.Name</fieldItem>

        <!-- Wrong -->
<fieldItem>Account.Name</fieldItem>

3. Region vs Facet Types

Template Regions (header, main, sidebar):

<name>header</name>
<type>Region</type>

Component Facets (internal slots like fieldSection columns):

<name>Facet-12345</name>
<type>Facet</type>

Rule: If it's a template region name → Region. If it's a component slot → Facet.

4. fieldInstance Structure

Every fieldInstance requires:

<itemInstances>
   <fieldInstance>
      <fieldInstanceProperties>
         <name>uiBehavior</name>
         <value>none</value> <!-- none|readonly|required -->
      </fieldInstanceProperties>
      <fieldItem>Record.FieldName__c</fieldItem>
      <identifier>RecordFieldName_cField</identifier>
   </fieldInstance>
</itemInstances>

Rules:

  • Each fieldInstance in its own <itemInstances> wrapper
  • Must have fieldInstanceProperties with uiBehavior
  • Use Record.{Field} format

5. Unique Identifiers and Region Names (CRITICAL - PREVENTS DUPLICATE ERRORS)

EVERY identifier and region/facet name MUST be unique across the entire FlexiPage file.

Critical Rules:

  • NEVER create two <flexiPageRegions> blocks with the same <name>
  • If multiple components belong to same facet, combine them in ONE region with multiple <itemInstances>
  • NEVER reuse the same <identifier> value
  • Always read entire file first and extract ALL existing identifiers and names

Wrong - This WILL FAIL with duplicate name error:

<!-- First field section in detail tab -->
<flexiPageRegions>
   <itemInstances>
      <componentInstance>
         <identifier>flexipage_property_details_fieldSection</identifier>
         ...
      </componentInstance>
   </itemInstances>
   <name>detailTabContent</name>  <!-- ❌ DUPLICATE NAME -->
   <type>Facet</type>
</flexiPageRegions>

<!-- Second field section in detail tab -->
<flexiPageRegions>
   <itemInstances>
      <componentInstance>
         <identifier>flexipage_pricing_fieldSection</identifier>
         ...
      </componentInstance>
   </itemInstances>
   <name>detailTabContent</name>  <!-- ❌ DUPLICATE NAME - DEPLOYMENT FAILS -->
   <type>Facet</type>
</flexiPageRegions>

Correct - Combine itemInstances in ONE region:

<!-- Both field sections in same detail tab facet -->
<flexiPageRegions>
   <itemInstances>
      <componentInstance>
         <identifier>flexipage_property_details_fieldSection</identifier>
         ...
      </componentInstance>
   </itemInstances>
   <itemInstances>
      <componentInstance>
         <identifier>flexipage_pricing_fieldSection</identifier>
         ...
      </componentInstance>
   </itemInstances>
   <name>detailTabContent</name>  <!-- ✅ ONE REGION, MULTIPLE COMPONENTS -->
   <type>Facet</type>
</flexiPageRegions>

When to combine vs separate:

  • Combine: Components that logically belong to same tab/section (e.g., multiple field sections in detail tab)
  • Separate: Components that belong to different tabs/sections (e.g., detailTabContent vs relatedTabContent)

Common Deployment Errors

"We couldn't retrieve or load the information on the field"

Cause: Invalid field API name - field doesn't exist on the object or has incorrect spelling Fix: Use MCP tools or describe commands to discover valid fields, then update the field reference (see Field Selection Rules)

"Invalid field reference"

Cause: Used ObjectName.Field instead of Record.Field
Fix: Change to Record.{FieldApiName}

"Element fieldInstance is duplicated"

Cause: Multiple fieldInstances in one itemInstances
Fix: Each fieldInstance needs its own <itemInstances> wrapper

"Missing fieldInstanceProperties"

Cause: No uiBehavior specified
Fix: Add fieldInstanceProperties with uiBehavior

"Unused Facet"

Cause: Facet defined but not referenced by any component
Fix: Remove Facet or reference it in a component property

"XML parsing error"

Cause: Unencoded HTML/XML in property values
Fix: Manually encode <, >, &, ", ' in all <value> tags

"Cannot create component with namespace"

Cause: Invalid page name (don't use __c suffix in page names)
Fix: Use "Volunteer_Record_Page" not "Volunteer__c_Record_Page"

"Region specifies mode that parent doesn't support"

Cause: Added <mode> tag to region Fix: Remove <mode> tags - they're not needed for standard regions


Generating Unique Identifiers

CRITICAL: Before generating ANY new identifier or facet name, follow the rules in section 5 of "Critical XML Rules" above.

Identifier Generation Algorithm:

1. Extract ALL existing <identifier> AND <name> values from XML
2. Generate base name: {componentType}_{context}
   Examples: "relatedList_contacts", "richText_header", "tabs_main"
3. Find first available number:
   - Try "{base}_1"
   - If exists, try "{base}_2", "{base}_3", etc.
   - Use first available

Examples:

  • First contacts related list: relatedList_contacts_1
  • Second contacts related list: relatedList_contacts_2
  • Rich text in header: richText_header_1
  • Field section: fieldSection_details_1

Facet Naming - Two Patterns:

  1. Named facets (for major content areas):

    • detailTabContent (detail tab content)
    • maintabs (main tab container)
    • sidebartabs (sidebar tab container)
    • Use when facet represents meaningful content area
  2. UUID facets (for internal structure):

    • Format: Facet-{8hex}-{4hex}-{4hex}-{4hex}-{12hex}
    • Example: Facet-66d5a4b3-bf14-4665-ba75-1ceaa71b2cde
    • Use for field section columns, nested containers, anonymous slots

When adding components to existing files:

  • Check if target facet name already exists
  • If exists: Add new <itemInstances> to that existing region (see section 5 above for details)
  • If doesn't exist: Create new region with unique name

Region Selection

Parse regions from file - don't hardcode names. Templates vary:

  • flexipage:recordHomeTemplateDesktopheader, main, sidebar
  • runtime_service_fieldservice:...header, main, footer
  • Others may have different region names

Default placement: End of target region (after last <itemInstances>)

Insertion pattern:

<flexiPageRegions>
   <name>main</name>  <!-- or whatever region name exists -->
   <type>Region</type>
   <itemInstances><!-- Existing component 1 --></itemInstances>
   <itemInstances><!-- Existing component 2 --></itemInstances>
   <itemInstances>
      <!-- INSERT NEW COMPONENT HERE -->
   </itemInstances>
</flexiPageRegions>

Container Components with Facets

Components like tabs, accordions, field sections require facets.

Pattern:

<!-- 1. Component in region -->
<flexiPageRegions>
   <itemInstances>
      <componentInstance>
         <componentName>flexipage:tabset2</componentName>
         <identifier>tabs_main_1</identifier>
         <componentInstanceProperties>
            <name>tabs</name>
            <value>tab1_content</value>
            <value>tab2_content</value>
         </componentInstanceProperties>
      </componentInstance>
   </itemInstances>
   <name>main</name>
   <type>Region</type>
</flexiPageRegions>

        <!-- 2. Facets (siblings of region, NOT nested inside) -->
<flexiPageRegions>
<itemInstances><!-- Tab 1 content --></itemInstances>
<name>tab1_content</name>
<type>Facet</type>
</flexiPageRegions>

<flexiPageRegions>
<itemInstances><!-- Tab 2 content --></itemInstances>
<name>tab2_content</name>
<type>Facet</type>
</flexiPageRegions>

Critical: Facet regions are siblings of template regions at the same level, not nested inside them.

Component-Specific Tips

dynamicHighlights (RecordPage Header)

Location: Must be in header region. Explicit Fields (via CLI): Use the most important fields to show a summary of the record. The single primary field is used to identify the record, like a name. The secondary fields (max 12, recommended 6) are used as a summary of the record.

--primary-field Name
--secondary-fields Phone,Industry,AnnualRevenue

CLI generates Facets with field references automatically.

fieldSection

Use for: Displaying fields in columns. Structure: Three-level nesting:

  1. Template Region (Region type)
  2. Column Facets (Facet type)
  3. Field Facets (Facet type) Referenced in component property:
<componentInstanceProperties>
   <name>columns</name>
   <value>Facet-{uuid}</value>
</componentInstanceProperties>

rich Text component

Component name: flexipage:richText

Use for: Displaying HTML-formatted rich text content with support for text formatting, headings, lists, tables, images, links, forms, and multimedia elements. Preserves styling and layout. Escape all special characters in the default text.

Location: Can be used in any region on any page type (Home, Record, App, Community pages).

CLI generates the component directly without nested structures.

User: "Add a rich text component to force-app/.../Account_Record_Page.flexipage-meta.xml"

Structure: Single-level component (no facets):

  1. Component instance (flexipage:richText) with direct properties

XML Structure Example:

<itemInstances>
   <componentInstance>
      <componentInstanceProperties>
         <name>decorate</name>
         <value>true</value>
      </componentInstanceProperties>
      <componentName>flexipage:richText</componentName>
      <identifier>flexipage_richText</identifier>
   </componentInstance>
</itemInstances>

Identifier Pattern: flexipage_richText or flexipage_richText_{sequence}


Required Metadata Structure

<FlexiPage xmlns="http://soap.sforce.com/2006/04/metadata">
   <flexiPageRegions>
      <!-- Regions and components here -->
   </flexiPageRegions>
   <masterLabel>Page Label</masterLabel>
   <template>
      <name>flexipage:recordHomeTemplateDesktop</name>
   </template>
   <type>RecordPage</type>
   <sobjectType>Object__c</sobjectType> <!-- RecordPage only -->
</FlexiPage>

Page Types:

  • RecordPage - requires <sobjectType>
  • AppPage - no sobjectType
  • HomePage - no sobjectType

Validation Checklist

Before deploying:

  • [NEW PAGES ONLY] Used CLI to bootstrap - NEVER create FlexiPage XML from scratch
  • ALL identifiers are unique - no duplicate <identifier> values anywhere in file
  • ALL region/facet names are unique - no duplicate <name> values in <flexiPageRegions>
  • Multiple components in same facet are combined - ONE region with multiple <itemInstances>, NOT separate regions with same name
  • All field references use Record.{Field} format
  • Each fieldInstance has fieldInstanceProperties with uiBehavior
  • Each fieldInstance in own <itemInstances> wrapper
  • Template regions use <type>Region</type>
  • Component facets use <type>Facet</type>
  • Property values with HTML/XML are manually encoded
  • No <mode> tags in regions
  • No __c suffix in page names
  • Each Facet referenced by exactly one component property

Quick Reference: CLI Command

# RecordPage with fields
sf template generate flexipage \
  --name Account_Custom_Page \
  --template RecordPage \
  --sobject Account \
  --primary-field Name \
  --secondary-fields Phone,Industry,AnnualRevenue \
  --detail-fields Street,City,State,Name,Phone,Email

# AppPage
sf template generate flexipage \
  --name Sales_Dashboard \
  --template AppPage \
  --label "Sales Dashboard"

# HomePage
sf template generate flexipage \
  --name Custom_Home \
  --template HomePage \
  --description "Custom home for sales team"

All templates support:

  • --output-dir (default: current directory)
  • --api-version (default: latest)
  • --label (default: page name)
  • --description
提供Salesforce Metadata API 604种元数据类型的XML格式与字段文档。用于创建或修改*-meta.xml文件、查询类型结构及SFDX项目规范。避免用于SOQL或Apex代码等非元数据场景,需精准读取JSON特定节以节省Token。
编写或编辑 *-meta.xml 文件 询问特定元数据类型的字段或XML格式 提及 Salesforce metadata 或 sfdx project
skills/platform-metadata-api-context-get/SKILL.md
npx skills add forcedotcom/sf-skills --skill platform-metadata-api-context-get -g -y
SKILL.md
Frontmatter
{
    "name": "platform-metadata-api-context-get",
    "metadata": {
        "version": "1.0",
        "cliTools": [
            {
                "tool": [
                    "jq"
                ],
                "semver": ">=1.6"
            },
            {
                "tool": [
                    "python3"
                ],
                "semver": ">=3.8"
            }
        ],
        "minApiVersion": "67.0"
    },
    "description": "Salesforce Metadata API reference for creating, understanding, or modifying metadata XML files (.object-meta.xml, .flow-meta.xml, etc.) and any of 604 metadata types (CustomObject, Flow, ApexClass, ApexTrigger, Profile, PermissionSet, Layout, ValidationRule, RecordType, EmailTemplate, ...). Use for questions about a type's fields or XML format, or Salesforce DX project structure (force-app\/main\/default). TRIGGER when: authoring or editing any *-meta.xml file, asking what fields\/format a metadata type has, or whenever 'Salesforce metadata' or 'sfdx project' is mentioned. DO NOT TRIGGER when: SOQL\/DML\/runtime sObject access (use the Enterprise API skill), Tooling API developer records (ApexExecutionOverlayAction, TraceFlag), or non-XML logic (Apex code, LWC JS, Visualforce controllers)."
}

Salesforce Metadata API Skill

This skill provides comprehensive documentation for all 604 Salesforce Metadata API types. Use this skill to create, understand, and modify Salesforce metadata XML files in your Salesforce DX projects.

Overview

The Salesforce Metadata API allows you to retrieve, deploy, create, update, or delete customizations for your org. This skill gives you access to detailed documentation for each metadata type, including:

  • Field definitions and data types
  • Required vs. optional fields
  • WSDL schema definitions
  • Sample XML structures
  • File naming conventions
  • Directory locations in Salesforce DX projects

How to Use This Skill

CRITICAL: Section-Specific Consumption

ALWAYS consume only the specific sections you need from JSON files, NOT entire files.

CRITICAL: For assets/metadata_api/*.json files, always use jq or programmatic JSON parsing to extract only the specific sections you need. Do not load these files whole via Read, cat, read_file, or any other tool that injects the complete file — they contain verbose WSDL segments and other sections that waste 60-80% of tokens. (Loading small files like this SKILL.md or the index table with Read is fine; the rule applies specifically to the large metadata-type JSON files.)

Each JSON file contains multiple sections (fields, description, wsdl_segment, etc.). Most use cases only require 1-2 sections:

  • For field definitions: Load only the fields section
  • For understanding purpose: Load only the description section
  • For XML examples: Load only the declarative_metadata_sample_definition section
  • Skip by default: wsdl_segment (verbose schema), file_information, directory_location

This reduces token consumption by 60-80% per file.

Quick Start

To get information about a specific metadata type:

  1. Section-specific (BEST): "Show me only the 'fields' section from CustomObject.json"
  2. Multiple sections: "Show me 'fields' and 'description' from Flow.json"
  3. Avoid loading entire files: Don't ask for "the CustomObject metadata type" - specify sections

Example Queries (Section-Specific)

  • ✅ "Show me only the 'fields' section from CustomObject.json"
  • ✅ "What fields are in the 'fields' section of Profile.json?"
  • ✅ "Load the 'description' and 'fields' sections from Flow.json"
  • ✅ "Give me just the 'declarative_metadata_sample_definition' from ApexClass.json"
  • ❌ "Show me the CustomObject metadata type" (too broad - entire file)
  • ❌ "Load CustomObject.json" (includes unnecessary WSDL and other sections)

JSON File Structure

Each metadata type is stored as a JSON file in assets/metadata_api/ with the following structure:

{
  "sections": ["title", "description", "fields", "wsdl_segment", ...],
  "title": "MetadataTypeName - Metadata API",
  "description": "Plain-text description of the metadata type.",
  "fields": {
    "fieldName": {
      "type": "string",
      "description": "Field description",
      "required": true
    }
  },
  "file_information": ".object",
  "directory_location": "objects",
  "wsdl_segment": "<xsd:complexType>...</xsd:complexType>",
  "declarative_metadata_sample_definition": [
    {
      "description": "Example description",
      "code": "<?xml version=\"1.0\"?>\n<MetadataType>...\n</MetadataType>"
    }
  ]
}

Note: string values (title, description, file_information, directory_location, wsdl_segment) are stored as plain text — no markdown headers (#/##) or code fences. file_information holds just the file suffix (e.g. .object, .ai) and directory_location just the SFDX folder name (e.g. objects, aiApplications).

Available Sections

The sections array indicates which top-level keys are present in each file. Common sections include:

  • title: The metadata type name and header
  • description: What the metadata type represents
  • fields: The type's own fields, with types and descriptions
  • sub_types: (composite types only) a map of referenced sub-type name → that sub-type's fields, e.g. Flowsub_types.FlowActionCall
  • file_information: File naming conventions and extensions
  • directory_location: Where files are stored in SFDX projects
  • wsdl_segment: XML schema definition from the WSDL
  • declarative_metadata_sample_definition: Example XML code

Some metadata types have additional sections specific to their functionality. See the Index Table for a complete breakdown.

More detail: background on why token optimization matters, worked usage examples, common workflows, a full section glossary, and versioning/support notes live in references/usage_guide.md. Load it with the Read tool only when needed.

Token Optimization Strategies

CRITICAL: To minimize token usage and costs:

  1. Load only the specific metadata type(s) you need, not the full corpus
  2. Load only specific sections from each file, not entire files

Section-Specific Loading (BEST PRACTICE)

⚠️ CRITICAL WARNING: DO NOT use the read_file tool (or any whole-file reading tool) on these JSON files!

read_file loads the entire file content into your context, defeating the purpose of section-specific consumption. You will waste 60-80% of your token budget loading unnecessary WSDL segments and verbose sections. (Using Read on small files such as this SKILL.md or the index table is fine — this rule is only about the large metadata-type JSON files.)

Approach: Programmatically parse the JSON file and extract ONLY the sections you need using code, not whole-file reading tools.

Working Examples Available:

We provide complete, working code examples in multiple languages:

See examples/README.md for complete documentation and usage instructions.

Quick Pattern (adapt to your language):

  1. Read the JSON file
  2. Parse it into a data structure
  3. Extract ONLY the sections you need (e.g., fields, description)
  4. Ignore verbose sections (wsdl_segment, declarative_metadata_sample_definition)

What NOT to Do

❌ NEVER use the read_file tool on these JSON files:

read_file assets/metadata_api/CustomObject.json  # Loads entire file into context!
read_file assets/metadata_api/Flow.json          # Wastes 60-80% tokens!

❌ NEVER load all files:

read_file assets/metadata_api/*.json  # This loads ~15MB of data!

Token Impact:

  • Section-specific: 50-200 tokens per metadata type
  • Entire file: 500-2000 tokens per metadata type
  • Savings: 60-80% per file

When to Load Multiple Types

  • Related types: CustomObject + CustomField + ValidationRule
  • Permission sets: Profile + PermissionSet + PermissionSetGroup
  • UI components: Layout + CompactLayout + QuickAction
  • Automation: Flow + WorkflowRule + ApexTrigger

When to Load Specific Sections (STRONGLY RECOMMENDED)

Many metadata types have large WSDL segments or extensive field lists. Always load only the specific sections you need from each JSON file rather than consuming the entire file:

  1. First, check available sections by reading just the sections array from the JSON
  2. Extract only the sections you need (e.g., fields for field definitions, description for overview)
  3. Skip WSDL segments unless you specifically need schema validation
  4. Skip declarative_metadata_sample_definition unless you need complete XML examples

This approach can reduce token consumption by 60-80% per file by excluding verbose WSDL definitions and lengthy examples.

Conceptual Approach to Using This Skill

Step 1: Identify Your Need

Ask yourself:

  • What am I trying to build or modify?
  • Which Salesforce metadata type(s) am I working with?
  • Which specific information do I need?
    • Field definitions only? → Load fields section
    • Understanding what it does? → Load description section
    • XML example? → Load declarative_metadata_sample_definition section
    • Schema validation? → Load wsdl_segment section (rarely needed)

Step 2: Find the Right Type

Use one of these methods:

  • Direct reference: If you know the type name (e.g., "CustomObject")
  • Index search: Check references/metadata_index_table.md for related types
  • Common types: See the "Quick Reference: Common Metadata Types" section below

Step 3: Load Selectively (Section-Specific)

Decision Tree for Section Loading:

Need field definitions?
  → Load ONLY 'fields' section (~50-200 tokens)

Need to understand what the type does?
  → Load ONLY 'description' section (~20-100 tokens)

Need XML structure example?
  → Load ONLY 'declarative_metadata_sample_definition' (~100-300 tokens)

Need all three?
  → Load 'fields' + 'description' + 'declarative_metadata_sample_definition'
  → Still skip 'wsdl_segment', 'file_information', 'directory_location'
  → Savings: ~60-70% vs loading entire file

Need schema validation?
  → Only then load 'wsdl_segment' (this is verbose)

Request format:

  • Single section (BEST): "Show me only the 'fields' section from ApexClass.json"
  • Multiple sections: "Load 'fields' and 'description' from CustomObject.json"
  • Skip verbose sections: Never load wsdl_segment unless explicitly needed

Step 4: Apply to Your Code

Use the loaded information to:

  • Create new metadata XML files
  • Understand existing files in your project
  • Validate field names and types
  • Generate correct XML structure with proper namespaces

File Location

All metadata type JSON files are located in:

assets/metadata_api/
├── CustomObject.json
├── Flow.json
├── ApexClass.json
├── Profile.json
└── ... (600 more files)

Path Resolution

When using this skill, files are referenced as:

  • Absolute: assets/metadata_api/CustomObject.json
  • Relative to skill root: ./assets/metadata_api/CustomObject.json

The skill will automatically resolve paths based on the working directory.

Metadata File Generation Requirements

When generating Salesforce metadata XML files, follow these requirements to ensure valid, deployable files.

XML Structure Requirements

All metadata files must:

  1. Include XML declaration:

    <?xml version="1.0" encoding="UTF-8"?>
    
  2. Use correct namespace:

    <CustomObject xmlns="http://soap.sforce.com/2006/04/metadata">
    
  3. Match root element to metadata type:

    • CustomObject → <CustomObject>
    • Flow → <Flow>
    • Profile → <Profile>
    • etc.

Namespace Declaration

The namespace is required and must be exactly:

http://soap.sforce.com/2006/04/metadata

Correct:

<CustomObject xmlns="http://soap.sforce.com/2006/04/metadata">

Incorrect:

<CustomObject>  <!-- Missing namespace -->
<CustomObject xmlns="http://salesforce.com/metadata">  <!-- Wrong namespace -->

Required vs. Optional Fields

Each metadata type has different field requirements:

  • Schema-required (required: true in the JSON): the WSDL marks the field as required.
  • Effectively required (not flagged but practically needed): in many cases the WSDL marks fewer fields as required than the authoring contract actually demands. CustomObject is the canonical example — the JSON marks only externalDataSource, externalName, nameField as required: true (the first two are external-object-only quirks), but a normal __c CustomObject also needs label, pluralLabel, deploymentStatus, and sharingModel to deploy. Always cross-check with the declarative_metadata_sample_definition examples.
  • Conditionally required: some fields are required only when certain features are enabled.
  • Optional: most fields can be omitted if not needed.

Example from CustomObject (note: practical authoring needs more than what required: true marks):

{
  "fields": {
    "nameField": {
      "type": "CustomField",
      "description": "The name field for the custom object",
      "required": true
    },
    "label": {
      "type": "string",
      "description": "The label for the custom object (effectively required for normal __c objects)",
      "required": false
    },
    "sharingModel": {
      "type": "SharingModel (enumeration)",
      "description": "The sharing model for the object (effectively required for normal __c objects)",
      "required": false
    },
    "enableHistory": {
      "type": "boolean",
      "description": "Enable field history tracking",
      "required": false
    }
  }
}

Validation Tips

Before deploying:

  1. Validate XML syntax: Ensure well-formed XML (matching tags, proper nesting)
  2. Check required fields: Verify all required fields are present
  3. Verify namespaces: Namespace must be exact
  4. Test field types: Ensure field values match expected types
  5. Use Salesforce CLI: Run sf project deploy validate to catch errors

More detail: field-type→XML mapping tables, file-naming/two-file/child-type conventions, and full well-formed-file examples are in references/usage_guide.md.

Duplicate and Ambiguous Type Names

Some Metadata API type names also exist as Enterprise/Data API or Tooling API object names. Examples include ApexClass, ApexTrigger, CustomField, CustomObject, EmailTemplate, Layout, Profile, PermissionSet, RecordType, StaticResource, WebLink, ValidationRule, and Flow.

When the prompt is ambiguous (e.g., "tell me about Profile" or "what fields are on ApexClass"), ask whether the user wants:

  1. Metadata API XML structure for source/deployment authoring (this skill, e.g. .profile-meta.xml, .cls-meta.xml).
  2. Enterprise/Data API runtime sObject/record reference (no dedicated skill currently — fall back to the Salesforce API family router).
  3. Tooling API developer tooling record reference (no dedicated skill currently — fall back to the Salesforce API family router).

Heuristics that resolve most ambiguity without asking:

  • Mentions of package.xml, force-app/, sfdx, .meta.xml, "deploy", "retrieve", "authoring", "blueprint", "template", "class definition", or "permissions" in a deployment sense → Metadata API (this skill).
  • "What fields are on X" / "what columns" / "DML" / "SOQL" / "query" / "REST" / "sObject" / "record" / "runtime" → Enterprise/Data or Tooling API (other skill).
  • Tooling-specific signals: "Tooling API", ApexCodeCoverage, EntityDefinition, TraceFlag, "code coverage", "compile errors", SymbolTable, debug logging → Tooling API.

Default-when-no-signals rule: if the prompt has none of the signals above AND this skill (platform-metadata-api-context-get) was invoked directly by name, default to the Metadata API interpretation and explicitly disclose the assumption to the user (e.g., "Interpreting this as the Metadata API type for .cls-meta.xml authoring; let me know if you meant the Tooling API record or Enterprise/Data sObject"). The skill-invocation context itself is a signal of authoring/deployment intent.

Troubleshooting

File Not Found

Problem: Cannot find metadata type file

Solutions:

  • File names are case-sensitive PascalCase with no separators (e.g., CustomObject.json, NOT customobject.json, Custom_Object.json, or Custom-Object.json).
  • Before declaring "not found", consult references/metadata_index_table.md. Use this two-pass recovery algorithm against the index:
    1. Normalize-and-substring (handles case + separator variants): strip non-alphanumeric characters and lowercase both the query and each index entry, then look for substring matches. Resolves: customobject, Custom_Object, Custom-ObjectCustomObject.
    2. On miss, fuzzy-match (handles missing-letter typos): use difflib.get_close_matches(query_normalized, index_normalized, n=3, cutoff=0.7) or Levenshtein distance ≤ 2. Resolves: customfeldCustomField, apxclassApexClass. Pure substring matching cannot recover character deletions.
  • Multi-hit tiebreaker: when normalize-and-substring returns multiple matches (e.g., customobject matches both CustomObject and CustomObjectTranslation), prefer the entry whose normalized length equals the normalized query length; otherwise prefer the shortest match.
  • Some types have unexpected naming conventions (no underscores, no spaces, no abbreviations like "OAuth"); the index is the source of truth.

SOAP Envelope / Header Types (thin by design)

Two related patterns to recognize:

  1. Result types (AsyncResult, SaveResult, DeleteResult, UpsertResult, Error, DescribeMetadataResult, etc.) — fields is empty AND wsdl_segment is populated. These are SOAP response wrappers; their schema lives entirely in wsdl_segment. Consume that section if you need their structure. They are not deployable source files.
  2. SOAP request headers (AllOrNoneHeader, SessionHeader, CallOptions, DebuggingHeader, OwnerChangeOptions, etc.) — fields has 1–2 minimal entries, no wsdl_segment. These configure SOAP request behavior; they are call-time options, not metadata you author or deploy.

In both cases, the thin JSON output is correct. Don't try to author a .AsyncResult-meta.xml — these types have no source-file form.

Missing Section

Problem: Expected section not in JSON file

Solutions:

  • Check the sections array to see what's available
  • Not all metadata types have all sections
  • Some sections are type-specific (noted in index table)

Incomplete Field Information

Problem: Field definition lacks details

Solutions:

  • Check wsdl_segment for complete schema definition
  • Some fields have complex types defined in WSDL
  • Cross-reference with Salesforce documentation for enumerations

Following Sub-Type Pointers (e.g., ProfileObjectPermissions[])

When the fields section gives a complex type name like ProfileObjectPermissions[] or LayoutItem[] or ApprovalStep[], the sub-fields of that nested type are NOT in the fields section — they live in wsdl_segment for that complex type. The skill's "skip wsdl_segment by default" rule is for token economy on the simple-field path; for nested types you need to drill in.

Worked example — find the sub-fields of objectPermissions on Profile:

# 1. Get the field type name from the fields section
jq '.fields.objectPermissions' assets/metadata_api/Profile.json
# → {"type": "ProfileObjectPermissions[]", ...}

# 2. Pull just the matching complexType from wsdl_segment using grep -A
jq -r '.wsdl_segment' assets/metadata_api/Profile.json   | grep -A 30 'complexType name="ProfileObjectPermissions"'

The grep -A N window keeps token cost ~150 tokens instead of loading the whole wsdl_segment (which can be 5K+ tokens on large types). Use this pattern any time fields returns a Foo[] type and you need Foo's sub-fields.

XML Generation Errors

Problem: Generated XML fails validation

Solutions:

  • Verify namespace is exactly: http://soap.sforce.com/2006/04/metadata
  • Check all required fields are present
  • Ensure field values match expected types
  • Validate XML syntax (closing tags, proper nesting)

Deployment Failures

Problem: Metadata file won't deploy

Solutions:

  • Run sf project deploy validate first
  • Check Salesforce API version compatibility
  • Verify file naming matches conventions
  • Ensure directory structure matches SFDX format

Quick Reference: Common Metadata Types

Here are the most frequently used metadata types:

  • CustomObject: defines the schema for a custom sObject, including fields, relationships, and settings
  • Flow: automates business processes using a visual canvas of elements and connectors
  • ApexClass: compiled Apex server-side class; includes body, API version, and status
  • ApexTrigger: Apex code that executes before/after DML events on a specific sObject
  • Profile: controls object/field permissions, app visibility, and login settings for a user profile
  • PermissionSet: additive set of permissions granted to users independently of their profile
  • CustomField: defines a field on a standard or custom object, including type, picklist values, and formula
  • Layout: controls the arrangement of fields and related lists on a record detail/edit page
  • ValidationRule: enforces data quality by preventing saves when a formula condition is true
  • ApexPage: Visualforce page definition, including controller reference and markup
  • ApexComponent: reusable Visualforce component that can be embedded in pages
  • CustomTab: defines a tab pointing to a custom object, Visualforce page, or web URL
  • CustomApplication: defines an app's tab bar, nav items, and branding
  • LightningComponentBundle: LWC bundle including JS, HTML, and metadata descriptor
  • AuraDefinitionBundle: Aura (Lightning) component bundle with component, controller, helper files
  • StaticResource: uploaded file (JS, CSS, image, ZIP) accessible from Visualforce and LWC
  • EmailTemplate: email template for use in workflow rules, Process Builder, or Apex
  • Report: saved report definition including filters, groupings, and columns
  • Dashboard: collection of dashboard components backed by reports

For a complete list of all metadata types, see Index Table.

通过sf project retrieve start命令从Salesforce组织检索元数据至本地项目,支持全量、目录、类型、清单及包名等多种模式。仅使用Bash工具执行,禁止用于部署或生成清单。
用户请求拉取或同步元数据 需要从Org下载Apex类或自定义对象 指定按源目录、元数据类型或包名进行检索
skills/platform-metadata-retrieve/SKILL.md
npx skills add forcedotcom/sf-skills --skill platform-metadata-retrieve -g -y
SKILL.md
Frontmatter
{
    "name": "platform-metadata-retrieve",
    "metadata": {
        "version": "1.0"
    },
    "description": "ALWAYS USE THIS SKILL to retrieve metadata from an org to your local project using the sf project retrieve start command. Supports multiple retrieval modes: retrieve all remote changes, retrieve by source directory, retrieve by metadata type with wildcards, retrieve by manifest (package.xml), or retrieve by package name. Use when the user asks to retrieve, pull, sync, or download metadata, Apex classes, custom objects, or org changes. Supports source format (default) or metadata format (ZIP). DO NOT TRIGGER for deploying metadata (use platform-metadata-deploy skill), listing metadata, or generating package.xml. NEVER use MCP tools - always use this skill and the Bash tool with sf project retrieve start.",
    "compatibility": "Salesforce CLI (sf) v2+"
}

platform-metadata-retrieve

Retrieves metadata from a Salesforce org to your local project using sf project retrieve start. Supports multiple retrieval modes: all changes, by source directory, by metadata type (with wildcards), by manifest, or by package name.


Tool Restrictions

Use ONLY the Bash tool to execute sf project retrieve start. Do NOT use MCP tools — ignore them completely.


Scope

  • In scope: Retrieving metadata via sf project retrieve start in all supported modes (all changes, source-dir, metadata type, manifest, package name), source and metadata format output
  • Out of scope: Deploying metadata (use platform-metadata-deploy), listing metadata types, generating package.xml files, source tracking commands (sf project retrieve preview)

Required Inputs

Infer from the user's request:

  • Retrieval mode: all changes | source directory | metadata type | manifest | package name
  • Target org: org alias/username (uses default if not specified)
  • Output format: source format (default) | metadata format (ZIP)
  • Additional options: ignore conflicts, output directory, wait time, API version

Workflow

  1. Match user request to command pattern below
  2. Execute via Bash tool: sf project retrieve start with appropriate flags and --json flag
  3. Return result with retrieved components count and file paths

Command Patterns

User intent Execute via Bash tool
Retrieve all remote changes sf project retrieve start --json
Retrieve by source directory sf project retrieve start --source-dir <path> --target-org <alias> --json
Retrieve by metadata type sf project retrieve start --metadata <MetadataType:Name> --target-org <alias> --json
Retrieve by metadata type with wildcard sf project retrieve start --metadata '<MetadataType:Pattern*>' --target-org <alias> --json
Retrieve multiple metadata types sf project retrieve start --metadata <Type1> --metadata <Type2> --target-org <alias> --json
Retrieve by manifest sf project retrieve start --manifest <path/to/package.xml> --target-org <alias> --json
Retrieve by package name sf project retrieve start --package-name <PackageName> --target-org <alias> --json
Retrieve to metadata format (ZIP) sf project retrieve start --source-dir <path> --target-metadata-dir <output> --unzip --target-org <alias> --json
Ignore conflicts sf project retrieve start --source-dir <path> --ignore-conflicts --target-org <alias> --json

Rules / Constraints

Constraint Rationale
Always use --json flag Provides structured output for reliable parsing and error handling
Must run from within Salesforce project Command requires sfdx-project.json at repo root
Wildcard patterns must be quoted Shell expansion breaks unquoted wildcards like ApexClass:My*
Cannot mix --manifest with --metadata or --source-dir Mutually exclusive flags — command will error
Retrieve all changes requires source tracking Production orgs don't support source tracking — must use other retrieval modes
--ignore-conflicts only works on trackable orgs No effect on production orgs; applies to scratch/sandbox only
--output-dir must be inside project directory Command validates output path is within project boundary
--output-dir cannot match package directory Command fails if target matches sfdx-project.json packageDirectories
Default wait time is 33 minutes Use --wait flag to override for large retrievals
Package retrieval is for reference only Retrieved package metadata should not be added to source control for development
CustomField retrieval auto-includes CustomObject When retrieving CustomField, CLI automatically adds CustomObject to get full context

Troubleshooting

Issue Resolution
"This command is required to run from within an SFDX project" Not in Salesforce project directory — cd to project root with sfdx-project.json
"No org found for " error Org alias doesn't exist or isn't authenticated — verify with sf org list
"This org does not support source tracking" Production org doesn't allow "retrieve all changes" mode — use --source-dir, --metadata, or --manifest instead
"ERROR running project retrieve start: Cannot mix --manifest with --metadata or --source-dir" Remove conflicting flags — use one retrieval mode only
Wildcard pattern retrieves nothing Pattern not quoted — wrap in single quotes: 'ApexClass:My*'
"The package directory path in sfdx-project.json does not exist" Output directory conflicts with package directory — use different path
"Output directory must be inside the project" --output-dir path is outside project boundary — use relative path inside project
Retrieve times out Increase wait time with --wait 60 for large metadata volumes
Retrieved files overwrite local changes Use --output-dir to retrieve to separate location, or commit local changes first
SourceConflictError with conflict table Conflicts detected between local and remote on trackable org (scratch/sandbox) — resolve conflicts manually or use --ignore-conflicts to force overwrite

Output Expectations

The command returns JSON output with retrieved components details.

See examples/success_output.json and examples/error_output.json for response structures.


Cross-Skill Integration

Need Delegate to
Deploy metadata to org platform-metadata-deploy skill
Preview retrieve without executing Execute sf project retrieve preview --target-org <alias> --json
List available metadata types Execute sf org list metadata-types --target-org <alias> --json

Reference File Index

File When to read
examples/success_output.json To understand successful retrieve response structure
examples/error_output.json To handle common error scenarios
references/retrieval_modes.md For detailed explanation of all retrieval modes and when to use each
references/cli_flags.md For complete flag reference with usage patterns
用于生成和修改Salesforce Sharing Rules元数据,支持基于条件、角色/组或访客用户的记录级访问控制。涵盖创建、检索及追加规则至XML文件,适用于Experience Site配置。
用户提及共享规则或记录共享需求 需要配置超出组织默认宽度的记录级访问权限 要求为特定角色、组或访客用户设置可见性 涉及sharingRules或.sharingRules-meta.xml文件操作
skills/platform-sharing-rules-generate/SKILL.md
npx skills add forcedotcom/sf-skills --skill platform-sharing-rules-generate -g -y
SKILL.md
Frontmatter
{
    "name": "platform-sharing-rules-generate",
    "metadata": {
        "version": "1.0"
    },
    "description": "Use this skill when users need to create, generate, or modify Salesforce Sharing Rules metadata. TRIGGER when: users mention sharing rules, record sharing, criteria-based sharing, role-based sharing, guest user sharing, portal user sharing, sharingRules, sharingCriteriaRules, sharingGuestRules, sharingOwnerRules, .sharingRules-meta.xml files, or ask to share records with specific roles or groups. Also trigger when users want to configure record-level access beyond org-wide defaults (OWD), share object records with roles, groups, or guest users, or set up Experience Site guest user record visibility. SKIP when: user needs permission sets or profiles (use platform-permission-set-generate), or needs object-level security rather than record-level sharing (use platform-permission-set-generate)."
}

Sharing Rules Generator

Generate Salesforce Sharing Rules metadata to control record-level access beyond org-wide defaults. Supports criteria-based rules, role/group-based owner rules, and guest user rules for Experience Sites.

Scope

  • In scope: Generating sharingCriteriaRules, sharingOwnerRules, and sharingGuestRules metadata; retrieving existing sharing rules from an org; appending new rules to existing files; configuring rules for Guest and Portal profiles.
  • Out of scope: Changing org-wide defaults (OWD/sharing model), creating Experience Sites, configuring permission sets or profiles (use platform-permission-set-generate), territory-based sharing rules.

Clarifying Questions

Before generating, confirm with the user if not already clear:

  • Which object should the sharing rule apply to? (standard or custom object API name)
  • What type of rule? (criteria-based, role/group-based owner rule, or guest user rule)
  • Who should records be shared with? (role name, group, portal role, or guest user nickname)
  • What access level? (Read or Read/Write)
  • For criteria-based rules: what field conditions should match?

Required Inputs

Gather or infer before proceeding:

  • Object API name: The sObject the rule targets (e.g., Account, Property__c)
  • Rule type: One of sharingCriteriaRules, sharingOwnerRules, or sharingGuestRules
  • Shared-to target: Role, group, portal role, or guest user community nickname
  • Access level: Read or Edit (maps to Read-Only or Read/Write)
  • Criteria (for criteria/guest rules): Field name, operation, and value for each filter item

Defaults unless specified:

  • Access level: Read
  • includeRecordsOwnedByAll: true for criteria rules
  • includeHVUOwnedRecords: false for guest rules
  • Account sharing rules include accountSettings with all sub-access levels set to None

Workflow

All steps are sequential. Do not skip or reorder.

Phase 1 — Discover

  1. Resolve the SFDX project path — find the project's sfdx-project.json and identify the package directory for sharingRules/.

  2. Check for existing sharing rules — look for <packageDir>/sharingRules/<ObjectName>.sharingRules-meta.xml. If found, read it to understand existing rules and avoid duplicates.

  3. If no local file exists, retrieve from the org:

    sf project retrieve start --metadata "SharingRules:<ObjectName>" --target-org <org>
    

Phase 2 — Determine Rule Type

  1. Select the rule type based on user intent. Read references/rule-types.md for the complete schema of each type and its required elements.

  2. For Account sharing rules: the accountSettings element is required. Default sub-access levels to None unless the user specifies otherwise.

  3. For Guest rules: the sharedTo must use <guestUser> with the site guest user's community nickname. Never use <role> or <group> for guest rules.

Phase 3 — Generate

  1. Construct the XML following the schema in references/rule-types.md. Key structure:

    • One .sharingRules-meta.xml file per object
    • All rules for the same object go in the same file
    • If appending to an existing file, add the new rule element inside the existing <SharingRules> root
  2. Name the rule — derive <fullName> from the intent (PascalCase, no spaces, descriptive). Generate a matching <label> in Title Case with spaces.

  3. Write the file to <packageDir>/sharingRules/<ObjectName>.sharingRules-meta.xml.

Phase 4 — Verify

  1. Run the verification checklist below before presenting output.

Verification Checklist

Universal Checks

  • Does the file have the XML declaration and <SharingRules xmlns="http://soap.sforce.com/2006/04/metadata"> root?
  • Is there exactly one file per object with all rules inside it?
  • Does <fullName> use PascalCase with no spaces?
  • Is <label> present and human-readable?
  • Is <accessLevel> one of Read or Edit?

Criteria Rule Checks

  • Is <includeRecordsOwnedByAll> present (required boolean)?
  • Does each <criteriaItems> have <field>, <operation>, and <value>?
  • Are picklist values valid for the target org?

Guest Rule Checks CRITICAL

  • Does <sharedTo> use <guestUser> (NOT <role> or <group>)?
  • Is <includeHVUOwnedRecords> present (required boolean)?
  • Is <includeRecordsOwnedByAll> ABSENT (only for criteria rules, not guest rules)?

Owner Rule Checks

  • Does the rule have both <sharedFrom> and <sharedTo> elements?
  • Do both use valid <role>, <roleAndSubordinates>, or <group> targets?

Account-Specific Checks CRITICAL

  • If object is Account, is <accountSettings> present with all three sub-elements?
  • Are <caseAccessLevel>, <contactAccessLevel>, <opportunityAccessLevel> all set?

Rules / Constraints

Constraint Rationale
One .sharingRules-meta.xml file per object Platform requirement — multiple files cause deployment errors
Guest rules must use <guestUser> in sharedTo Using <role> or <group> causes: "Specify a guest user's nickname for the guestUser field"
Account rules require <accountSettings> Without it: "AccountSettings is required for account sharing rules"
includeRecordsOwnedByAll is required on criteria rules Missing it causes: "Required field is missing: sharingCriteriaRules"
includeHVUOwnedRecords is required on guest rules Missing it causes deployment failure
Criteria field values must exist as picklist values on the org Invalid values cause: "Picklist value does not exist"
Never hardcode file paths — resolve from sfdx-project.json Customer projects use custom package directories

Gotchas

Issue Resolution
Guest rule uses <role> instead of <guestUser> Replace with <guestUser>CommunityNickname</guestUser>
Account rule missing accountSettings Add <accountSettings> with all three access level sub-elements set to None
Criteria rule missing includeRecordsOwnedByAll Add <includeRecordsOwnedByAll>true</includeRecordsOwnedByAll>
Picklist value mismatch Query the org for valid values before generating criteria
Appending duplicates existing rule name Check existing <fullName> values before writing
Guest user nickname not found Query: SELECT CommunityNickname FROM User WHERE UserType='Guest' AND IsActive=true

Output Expectations

Deliverables:

  • <packageDir>/sharingRules/<ObjectName>.sharingRules-meta.xml — complete sharing rules file for the target object

Cross-Skill Integration

Need Delegate to
Permission set configuration platform-permission-set-generate skill
Custom object creation (if target object doesn't exist) platform-custom-object-generate skill

Reference File Index

File When to read
references/rule-types.md Phase 2 — before generating any rule, to get the complete XML schema for each rule type
专注Salesforce SOQL/SOSL的生成、优化与分析。支持自然语言转查询、关系及聚合查询、执行计划分析和性能安全改进。适用于查询编写、调试及性能优化场景,排除批量数据处理和Apex逻辑嵌入任务。
用户需要编写或优化SOQL/SOSL查询 涉及.soql文件处理 询问关系查询、聚合查询或查询性能问题
skills/platform-soql-query/SKILL.md
npx skills add forcedotcom/sf-skills --skill platform-soql-query -g -y
SKILL.md
Frontmatter
{
    "name": "platform-soql-query",
    "metadata": {
        "version": "1.1"
    },
    "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."
}

platform-soql-query: Salesforce SOQL Query Expert

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/safety improvements for Salesforce queries.

When This Skill Owns the Task

Use platform-soql-query when the work involves:

  • .soql files
  • query generation from natural language
  • relationship queries and aggregate queries
  • query optimization and selectivity analysis
  • SOQL/SOSL syntax and governor-aware design

Delegate elsewhere when the user is:


Required Context to Gather First

Ask for or infer:

  • target object(s)
  • fields needed
  • filter criteria
  • sort / limit requirements
  • whether the query is for display, automation, reporting-like analysis, or Apex usage
  • whether performance / selectivity is already a concern

Recommended Workflow

1. Generate the simplest correct query

Prefer:

  • only needed fields
  • clear WHERE criteria
  • reasonable LIMIT when appropriate
  • relationship depth only as deep as necessary

2. Choose the right query shape

Need Default pattern
parent data from child child-to-parent traversal
child rows from parent subquery
counts / rollups aggregate query
records with / without related rows semi-join / anti-join
text search across objects SOSL

3. Optimize for selectivity and safety

Check:

  • indexed / selective filters
  • no unnecessary fields
  • no avoidable wildcard or scan-heavy patterns
  • security enforcement expectations

4. Validate execution path if needed

If the user wants runtime verification, hand off execution to:


High-Signal Rules

  • never use SELECT * style thinking; query only required fields
  • do not query inside loops in Apex contexts
  • prefer filtering in SOQL rather than post-filtering in Apex
  • use aggregates for counts and grouped summaries instead of loading unnecessary records
  • evaluate wildcard usage carefully; leading wildcards often defeat indexes
  • account for security mode / field access requirements when queries move into Apex

Output Format

When finishing, report in this order:

  1. Query purpose
  2. Final SOQL/SOSL
  3. Why this shape was chosen
  4. Optimization or security notes
  5. Execution suggestion if needed

Suggested shape — use references/soql-syntax-reference.md for exact syntax:

Query goal: <summary>
Query: <soql or sosl>
Design: <relationship / aggregate / filter choices>
Notes: <selectivity, limits, security, governor awareness>
Next step: <run in platform-data-manage or embed in Apex>

Cross-Skill Integration

Need Delegate to Reason
run the query against an org platform-data-manage execution and export
embed the query in services/selectors platform-apex-generate implementation context
analyze slow-query symptoms from logs platform-apex-logs-debug runtime evidence
wire query-backed UI experience-lwc-generate frontend integration

Score Guide

Score Meaning
90+ production-optimized query
80–89 good query with minor improvements possible
70–79 functional but performance concerns remain
< 70 needs revision before production use

Reference File Index

File When to read
references/soql-syntax-reference.md Syntax, operators, date literals, relationship query patterns
references/query-optimization.md Selectivity rules, indexing strategy, governor limits, security patterns
references/soql-reference.md Quick reference — operators, date functions, aggregate functions, WITH clauses
references/anti-patterns.md Common SOQL mistakes and their fixes — read before finalizing any query
references/selector-patterns.md Apex selector layer patterns — read when embedding queries in Apex classes
references/field-coverage-rules.md Field coverage validation — read when generating SOQL used inside Apex code
references/cli-commands.md sf CLI query execution, bulk export, query plan commands
assets/basic-queries.soql Starter query examples for common objects
assets/relationship-queries.soql Parent-to-child and child-to-parent relationship query patterns
assets/aggregate-queries.soql COUNT, SUM, GROUP BY, ROLLUP query patterns
assets/optimization-patterns.soql Selective filter and index-aware query patterns
assets/bulkified-query-pattern.cls Apex Map-based bulk query pattern for trigger contexts
assets/selector-class.cls Full selector class implementation template
scripts/post-tool-validate.py Post-write hook — runs static SOQL validation and live query plan analysis after .soql file edits
生成AgentforcePlatformTracingSettings元数据,用于启用或禁用Agentforce代理执行追踪跨度至Data Cloud。适用于配置平台可观测性追踪设置,需确保已配置Data Cloud及相应权限。
用户提及Agentforce追踪、代理追踪跨度、Data Cloud追踪、AgentforcePlatformTracingSettings 用户询问平台可观测性追踪、启用代理追踪或将代理执行跨度发送至Data Cloud
skills/platform-tracing-agentforce-configure/SKILL.md
npx skills add forcedotcom/sf-skills --skill platform-tracing-agentforce-configure -g -y
SKILL.md
Frontmatter
{
    "name": "platform-tracing-agentforce-configure",
    "metadata": {
        "version": "1.0",
        "minApiVersion": "68.0",
        "relatedSkills": "platform-tracing-configure"
    },
    "description": "Generate AgentforcePlatformTracingSettings metadata to enable or disable Agentforce agent execution trace spans flowing to Data Cloud. Use this skill for any AgentforcePlatformTracingSettings metadata work. TRIGGER when: user mentions Agentforce tracing, agent trace spans, Data Cloud tracing, AgentforcePlatformTracingSettings, platform observability tracing, enable agent tracing, wants agent execution spans in Data Cloud, mentions .settings-meta.xml for AgentforcePlatformTracing, or asks about enabling observability for Agentforce agents. DO NOT TRIGGER when: user wants Platform Tracing for TraceSpanEvent (use platform-tracing-configure), wants to query or analyze existing agent trace data in Data Cloud (use agentforce-observe), wants Event Log Files or ELF configuration, wants Change Data Capture (use integration-eventing-cdc-configure), or wants ManagedEventSubscription (use integration-eventing-subscription-configure)."
}

Platform Tracing — Agentforce Configure

Generate AgentforcePlatformTracingSettings metadata to enable or disable forwarding of Agentforce agent execution trace spans to Data Cloud's ingestion pipeline. This is a singleton Settings type with one boolean field introduced in API v68.0 (Spring '25).

Scope

  • In scope: Generating AgentforcePlatformTracing.settings-meta.xml to enable or disable Agentforce agent tracing.
  • Out of scope: Platform Tracing for TraceSpanEvent (use platform-tracing-configure). Event Log Files. Change Data Capture. Org permission provisioning. Data Cloud provisioning.

Prerequisites

Before generating, inform the user of these requirements. The skill cannot check org state, but deploying without these prerequisites means the setting has no effect:

  1. Data Cloud must be provisioned in the org — trace spans are forwarded to Data Cloud's ingestion pipeline. Without Data Cloud, there is no destination for the spans.
  2. PlatformObservability org permission must be active — this permission gates the feature. It is provisioned (not settable via metadata).
  3. API version 68.0+ — the AgentforcePlatformTracingSettings type was introduced in Spring '25. Orgs on older API versions will not recognize it.

If the user reports the setting isn't working after deploy, the most likely cause is a missing prerequisite above.


Clarifying Questions

Before generating, confirm with the user if not already clear:

  • Enable or disable? (Which state do you want for Agentforce agent tracing?)

No other clarification needed — this is a singleton type with one boolean field.


Required Inputs

Gather or infer before proceeding:

  • Desired state: true (enable) or false (disable)

Defaults unless specified:

  • If user says "enable" or "turn on": set to true
  • If user says "disable" or "turn off": set to false

If the user provides a clear request, generate immediately without unnecessary back-and-forth.


Workflow

  1. Warn about prerequisites — inform the user that this feature requires Data Cloud and the PlatformObservability org permission.

  2. Read the template — load assets/AgentforcePlatformTracing-template.xml.

  3. Generate the settings file — replace {ENABLED} with true or false based on the user's desired state.

  4. Place the file — output to settings/AgentforcePlatformTracing.settings-meta.xml in the project's source directory.


Rules / Constraints

Constraint Rationale
Singleton — only one file per org, one boolean field The metadata type has exactly one instance. Deploying the file sets the org-wide preference.
XML namespace must be http://soap.sforce.com/2006/04/metadata Any other namespace causes deploy failure.
File must be named AgentforcePlatformTracing.settings-meta.xml SFDX source format convention for this Settings type.
Only include enableAgentforcePlatformTracing field No other fields exist on this type.
Requires API v68.0+ Older orgs reject the metadata type entirely.

Gotchas

Issue Resolution
Deploy succeeds but tracing doesn't activate Org lacks PlatformObservability permission or Data Cloud is not provisioned. These are provisioned, not settable via metadata.
AgentforcePlatformTracingSettings type not recognized Org or tooling is on API version < 68.0. Update sfdx-project.json sourceApiVersion.
User confuses this with Platform Tracing (TraceSpanEvent) Clarify: this sends Agentforce agent execution spans to Data Cloud. For TraceSpanEvent publishing, use the platform-tracing-configure skill.

Output Expectations

Deliverables:

  • settings/AgentforcePlatformTracing.settings-meta.xml

Before delivering, verify:

  • XML namespace is exactly http://soap.sforce.com/2006/04/metadata
  • File is named AgentforcePlatformTracing.settings-meta.xml
  • Only enableAgentforcePlatformTracing is present (no extra fields)

Cross-Skill Integration

Need Delegate to
Enable TraceSpanEvent publishing (Platform Tracing) platform-tracing-configure skill
Query or analyze existing Agentforce agent trace data in Data Cloud agentforce-observe skill
Set up Change Data Capture integration-eventing-cdc-configure skill
Configure ManagedEventSubscription integration-eventing-subscription-configure skill

Reference File Index

File When to read
assets/AgentforcePlatformTracing-template.xml Step 2 — template for generating the settings file
生成EventSettings元数据以控制Platform Tracing开关。用于启用或禁用TraceSpanEvent发布,修改enablePlatformTracing字段。需确保Org拥有PlatformTracing权限且API版本68.0+。不涉及Agentforce追踪、ELF或CDC。
用户提到 Platform Tracing 用户提到 TraceSpanEvent 用户要求在 Event Monitoring Settings 中启用追踪 用户提到 event monitoring tracing toggle 用户提到 enablePlatformTracing 用户提到 .settings-meta.xml for Event settings tracing 用户要求 turn on trace span events 用户要求 stop publishing trace spans
skills/platform-tracing-configure/SKILL.md
npx skills add forcedotcom/sf-skills --skill platform-tracing-configure -g -y
SKILL.md
Frontmatter
{
    "name": "platform-tracing-configure",
    "metadata": {
        "version": "1.0",
        "minApiVersion": "68.0",
        "relatedSkills": "platform-tracing-agentforce-configure"
    },
    "description": "Generate EventSettings metadata to enable or disable Platform Tracing (TraceSpanEvent publishing) in Event Monitoring. Use this skill for any EventSettings enablePlatformTracing metadata work. TRIGGER when: user mentions Platform Tracing, TraceSpanEvent, enable tracing in Event Monitoring Settings, event monitoring tracing toggle, enablePlatformTracing, .settings-meta.xml for Event settings tracing, turn on trace span events, or stop publishing trace spans. DO NOT TRIGGER when: user wants Agentforce agent tracing to Data Cloud (use platform-tracing-agentforce-configure), wants Event Log Files or ELF generation, wants Change Data Capture (use integration-eventing-cdc-configure), or wants ManagedEventSubscription (use integration-eventing-subscription-configure)."
}

Platform Tracing — Configure

Generate EventSettings metadata to enable or disable the Platform Tracing toggle, which controls whether TraceSpanEvent is published at a sample rate. This modifies a single field (enablePlatformTracing) within the existing EventSettings metadata type.

Scope

  • In scope: Generating Event.settings-meta.xml with the enablePlatformTracing field to enable or disable TraceSpanEvent publishing.
  • Out of scope: Agentforce agent tracing to Data Cloud (use platform-tracing-agentforce-configure). Other EventSettings fields (enableDeleteMonitoringData, enableLoginForensics, etc.) owned by other teams. Event Log Files. Change Data Capture.

Prerequisites

Before generating, inform the user of these requirements:

  1. PlatformTracing org permission must be active — this permission gates the feature. It is provisioned (not settable via metadata). Without it, the setting deploys but TraceSpanEvent is not published.
  2. API version 68.0+enablePlatformTracing on EventSettings was introduced in Spring '25. Orgs or tooling on older API versions will not recognize the field. Update sfdx-project.json sourceApiVersion to 68.0 or higher if on an older tooling version.

If the user reports the setting isn't working after deploy, the most likely cause is a missing prerequisite above.


Clarifying Questions

Before generating, confirm with the user if not already clear:

  • Enable or disable? (Which state do you want for Platform Tracing / TraceSpanEvent?)

No other clarification needed — this controls a single boolean field.


Required Inputs

Gather or infer before proceeding:

  • Desired state: true (enable) or false (disable)

Defaults unless specified:

  • If user says "enable" or "turn on": set to true
  • If user says "disable" or "turn off": set to false

If the user provides a clear request, generate immediately without unnecessary back-and-forth.


Workflow

  1. Warn about prerequisites — inform the user that this feature requires the PlatformTracing org permission and API version 68.0+.

  2. Read the template — load assets/EventSettings-template.xml.

  3. Generate the settings file — replace {ENABLED} with true or false based on the user's desired state.

  4. Place the file — output to settings/Event.settings-meta.xml in the project's source directory.


Rules / Constraints

Constraint Rationale
Only include enablePlatformTracing in generated metadata Other fields in EventSettings are owned by other teams. Including them risks overwriting unrelated settings during deploy.
XML namespace must be http://soap.sforce.com/2006/04/metadata Any other namespace causes deploy failure.
File must be named Event.settings-meta.xml SFDX source format convention — the type name prefix for EventSettings is Event.
Requires PlatformTracing org permission Without this permission, the setting deploys but has no effect on TraceSpanEvent publishing.
Requires API v68.0+ Older orgs/tooling reject the field entirely.

Gotchas

Issue Resolution
Deploy succeeds but TraceSpanEvent not published Org lacks the PlatformTracing permission. This is provisioned, not settable via metadata.
enablePlatformTracing field not recognized by org/tooling Tooling is on API version < 68.0. Update sfdx-project.json sourceApiVersion to 68.0 or higher.
User confuses this with Agentforce tracing Clarify: this publishes TraceSpanEvent for platform operations. For Agentforce agent spans to Data Cloud, use the platform-tracing-agentforce-configure skill.
User wants to configure all Event Monitoring settings Only enablePlatformTracing is in scope. Other EventSettings fields are owned by other teams and not managed by this skill.

Output Expectations

Deliverables:

  • settings/Event.settings-meta.xml

Before delivering, verify:

  • XML namespace is exactly http://soap.sforce.com/2006/04/metadata
  • File is named Event.settings-meta.xml
  • Only enablePlatformTracing is present (no other EventSettings fields)
  • Project sourceApiVersion in sfdx-project.json is 68.0 or higher

Cross-Skill Integration

Need Delegate to
Enable Agentforce agent tracing to Data Cloud platform-tracing-agentforce-configure skill
Set up Change Data Capture integration-eventing-cdc-configure skill
Configure ManagedEventSubscription integration-eventing-subscription-configure skill

Reference File Index

File When to read
assets/EventSettings-template.xml Step 2 — template for generating the settings file
管理Salesforce Archive(可信服务存档),支持通过Connect API搜索、恢复、分析、掩码及RTBF擦除存档记录,并查询ArchiveActivity作业状态与日志。适用于存档数据操作及故障排查。
用户提及Salesforce Archive或Trusted Services Archive 请求搜索、查看、恢复或擦除存档记录 询问ArchiveActivity作业状态或下载失败日志 涉及RTBF(被遗忘权)或PII掩码操作 请求关于存档策略、分析器或存储的指南
skills/platform-trust-archive-manage/SKILL.md
npx skills add forcedotcom/sf-skills --skill platform-trust-archive-manage -g -y
SKILL.md
Frontmatter
{
    "name": "platform-trust-archive-manage",
    "metadata": {
        "version": "1.0"
    },
    "description": "ALWAYS USE THIS SKILL for anything involving Salesforce Archive (also called Trusted Services Archive) — search, view, unarchive, analyze, mask, and erase (RTBF) archived records via the Archive Connect API, and reading archive job status from the ArchiveActivity object. TRIGGER when: user mentions Salesforce Archive, Trusted Services Archive, archive\/unarchive records, ArchiveActivity, archive jobs, archive policy, archive analyzer, archived record search, archive storage, archive failure logs, right to be forgotten \/ RTBF on archived data, or masking archived PII — including phrasings like 'find records that were archived', 'restore archived data', 'why did the archive job fail', 'download the archive failure log', or 'monitor my archive jobs', AND even when they ask you to explain, give guidance, or write a runbook\/doc about these topics rather than run code. SKIP when: the user wants generic data-export\/backup unrelated to the Archive add-on, or wants to build the archive policy UI metadata."
}

Salesforce Archive

Operate Salesforce Archive (also called Trusted Services Archive) through its Connect API and the ArchiveActivity job-metadata object. This skill covers how to search and restore archived records, run the analyzer, handle RTBF erasure and PII masking, check storage, and — the part most often missed — how to read archive job status from ArchiveActivity and use a job's Id + Type to download its logs.

Scope

  • In scope: Calling the Archive Connect API operations under /platform/data-resilience/archive/; querying the ArchiveActivity object via SOQL/Connect; correlating a job's ArchiveActivity record with its log-download endpoints; the verify-after-write pattern for each async operation.
  • Out of scope: Defining archive policies / ArchivePolicyDefinition metadata; building UI; generating Flows over archive data (ArchiveActivity is not Flow-queryable — see Gotchas); generic backup/export tooling unrelated to the add-on.

Required Inputs

Gather or infer before acting:

  • Operation intent: search (this is also how you view archived records), unarchive, analyze, mask, RTBF, storage check, or job-status/log lookup.
  • Target sObject (sobjectName): required for search and unarchive.
  • Filters: search and unarchive require sobjectName + at least one filter.
  • For log downloads: the requestId (an ArchiveActivity Id, 8qv… prefix) of a completed, log-producing job, and reportType = that activity's Type.

Preconditions (confirm or surface to the user if a call returns a not-permitted error):

  • The org must have Salesforce Archive enabled. Every operation is gated on this first.
  • Each operation requires a specific user permission on top of the org gate — see the Permissions table below. There is no single "archive admin" role; access is per-capability.

Permissions

Every operation first requires the org to have Salesforce Archive enabled. On top of that org gate, each capability is gated by a distinct user permission. A call the user isn't permitted for fails with a "not permitted" error — match the error to the missing permission below.

Operation User permission required
search-archived-records, get-search-archived-records-next-page ViewSearchPage (Archive Search) — not ViewArchivedRecords
search-archived-records-with-sharing-rules ViewArchivedRecords
unarchive-records UnarchiveSdk
forget-archived-records (RTBF) + get-rtbf-status Rtbf
mask-archived-records + get-masking-status Rtbf (masking shares the same Rtbf permission — not a separate entitlement)
run-analyzer, get-analyzer-report, get-archive-storage-used ArchiveAnalyzer
get-execution-details-stream-url, get-failed-records-stream-url ViewActivitiesPage (Archive Activities)

Workflow

All steps are sequential within a task. Read the referenced file the first time you touch that area.

  1. Identify the operation and read the contract — do not rely on general knowledge of the Archive API, which has non-obvious contracts. Load references/connect-api-operations.md for the exact request/response shape, required inputs, and per-operation gotchas of every Archive Connect API operation. Do this before constructing any call (e.g. dateRanges plural vs singular, isSuccess flag vs HTTP status, url: null meaning no log).

  2. For job status / monitoring, read the data model — when the task involves archive jobs, failures, progress, counts, or logs, load references/archive-activity-entity.md for the ArchiveActivity field reference and how it links to the Connect API. Query ArchiveActivity via SOQL or Connect — not Flow. For a worked end-to-end example (find failed/in-progress jobs, then pull their execution-detail and failed-records logs), load examples/monitor-failed-jobs.md.

  3. Construct and send the call — every operation is a {method, path, body} REST call. Send it with whatever Connect/REST API tool your environment provides (an MCP server that invokes Connect/REST APIs, the sf CLI, or any REST client). Two path rules are critical (full per-operation contracts are in references/connect-api-operations.md):

    • The operation names in this skill are NOT URL paths. search-archived-records, unarchive-records, etc. are labels; never put them in the path. Use the short literal paths below (each relative to base /platform/data-resilience/archive). Sending the operation name as a path segment (e.g. /…/archive/search-archived-records) returns 404.
    • The path stops at /platform/data-resilience/archive/… — there is NO /connect segment, even though this is a Connect API. A 404 / NOT_FOUND here means the path is wrong, NOT that Archive is disabled — fix the path before concluding the add-on is missing.
    Operation Method + Path Notes
    search-archived-records POST /search requires sobjectName + ≥1 filter
    search next page GET /search/next/{scrollId} stop when scroll_id == "-1"
    search with sharing rules POST /search/with-sharing-rules uses filtersJson object map
    unarchive-records POST /unarchive sobjectName + filters
    run-analyzer / report POST /analyzer/run · GET /analyzer/report
    forget / RTBF + status POST /rtbf · GET /rtbf/{requestId}
    mask + status POST /mask · GET /mask/{requestId}
    storage used GET /storage/archive-used
    execution-detail / failed-records log GET /log/execution-details-stream-url · GET /log/failed-records-stream-url query params requestId, reportType

    With the sf CLI, prefix the path with /services/data/v67.0; some MCP/REST tools take the bare path and add the version themselves (tool-dependent — see the reference). Then follow the contract: for searches, supply sobjectName + ≥1 filter; for date filtering use the plural dateRanges array of {field, from, to} with full ISO-8601 datetimes.

  4. Branch on the right signal — some operations return HTTP 201 with a body-level success flag (body.statusCode, body.isSuccess). Read references/connect-api-operations.md for which signal to trust per operation; never assume the HTTP status alone means success.

  5. Verify after every write — re-read state to confirm the effect (see the Verify-After-Write table below). Async operations (analyzer, RTBF, masking) return a request id you must poll.


Verify-After-Write

After this write Confirm by
run-analyzer Poll get-analyzer-report until the report is populated
unarchive-records Re-run search-archived-records — confirm records left the archive
forget-archived-records (RTBF) Poll get-rtbf-status with the returned request_id
mask-archived-records Poll get-masking-status with the returned request_id

Rules / Constraints

Constraint Rationale
Search & unarchive require sobjectName + at least one filter An unfiltered request is rejected with "Search must be based on at least 1 field" — a full-object operation is never allowed.
Date filters must be full ISO-8601 datetimes (2020-01-01T00:00:00Z) A date-only value (2020-01-01) returns 400 JSON_PARSER_ERROR because the field is typed xsd:dateTime.
Search uses dateRanges (plural array); unarchive uses dateRange (singular) They are genuinely different fields on the two endpoints; using the wrong shape silently drops the filter or 400s.
Stop pagination when scroll_id == "-1" Calling get-search-archived-records-next-page with "-1" returns 500.
Log downloads need a real ArchiveActivity Id as requestId + that activity's Type as reportType The backend resolves the log by the activity record; a mismatched reportType returns no log.
Excluded objects are not retrievable Feed, History, Relation, Share are not searchable; Files/Attachments are not retrievable via this API — do not promise them.
Query ArchiveActivity via SOQL/Connect, never Flow ArchiveActivity has isProcessEnabled=false, so a Flow "Get Records" element on it fails with "You can't get ArchiveActivity records in a flow."

Gotchas

Issue Resolution
Treating HTTP 201 as success Several operations return 201 with a body-level outcome. Branch on body.statusCode (search) or body.isSuccess (with-sharing-rules), not the HTTP code.
run-analyzer.isRunning used as a signal It is always null; the endpoint only populates message. Poll get-analyzer-report to confirm completion instead.
search-archived-records-with-sharing-rules filters as an array filtersJson must be a JSON-encoded object map {"Field":"Value"}, not an array of {field,value}; the array form returns isSuccess:false "No valid filters provided".
Log url treated as present because status is 201 get-*-stream-url returns {url}; url: null means no log was resolved. Always check url != null.
Misreading get-archive-storage-used usedStorage[]/availableStorage[] are parallel positional arrays: index 0=org DATA, 1=org FILE, 2=archive RECORDS, 3=archive FILE. availableStorage[2]/[3] are always 0 (archive tier is unmetered) — that means "not tracked", not "full".
Expecting ArchiveActivity in a Flow It is not Flow-enabled (isProcessEnabled=false). Use SOQL/Connect/Reports.
Hitting unarchive caps Unarchive processes ≤1000 matched records per request and ≤50 requests/hour/org, and restores the whole archived hierarchy of each match.
RTBF/masking caps criteria ≤10 entries (one per object); ≤10,000 root records/day (shared between RTBF and masking); masking is irreversible. Both RTBF and masking are gated by the same Rtbf user permission.

Output Expectations

This is a knowledge/API skill — it produces API calls and their interpreted results, plus SOQL against ArchiveActivity. It does not generate deployable metadata. Deliverables per task: the correct operation invocation(s), the right success-signal branching, and a verify-after-write confirmation.


Reference File Index

File When to read
references/connect-api-operations.md Before constructing any Archive Connect API call — full per-operation contracts, success signals, and limits
references/archive-activity-entity.md For any job-status / failure / progress / log task — ArchiveActivity field reference and its link to the log-download endpoints
examples/monitor-failed-jobs.md To follow an end-to-end monitoring flow: find failed/in-progress jobs, then download their logs
用于生成和验证Salesforce可复用选择列表元数据,包括GlobalValueSet(新建共享值集)和StandardValueSet(修改标准值集),支持字段关联及部署错误排查。
用户需要创建或生成全局值集(GlobalValueSet) 用户需要定制标准值集(StandardValueSet) 提及可复用选择列表或跨字段共享的值集 处理标准选择列表自定义如Industry, Lead Source 遇到向标准选择列表添加值的部署错误 操作.globalValueSet-meta.xml或.standardValueSet-meta.xml文件
skills/platform-value-set-generate/SKILL.md
npx skills add forcedotcom/sf-skills --skill platform-value-set-generate -g -y
SKILL.md
Frontmatter
{
    "name": "platform-value-set-generate",
    "metadata": {
        "version": "1.0",
        "cliTools": [
            {
                "tool": [
                    "sf"
                ],
                "semver": ">=2.0.0"
            }
        ],
        "minApiVersion": "60.0"
    },
    "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."
}

Overview

Generates and validates the two reusable picklist value-set metadata types — GlobalValueSet (a new reusable set shared across fields) and StandardValueSet (customizing a built-in catalog picklist like Industry or Lead Source) — and wires a CustomField to one via <valueSetName>.

Scope

  • In scope: creating a GlobalValueSet, customizing a StandardValueSet, referencing either from a field, and the related deployment errors.
  • Out of scope: a one-off inline picklist on a single field with no reuse → use platform-custom-field-generate (inline <valueSetDefinition>). Generating the field that references a value set is also platform-custom-field-generate's job; this skill produces the value set itself.

Two different metadata types — do not confuse them:

Concern GlobalValueSet StandardValueSet
Folder globalValueSets/ standardValueSets/
File suffix .globalValueSet-meta.xml .standardValueSet-meta.xml
Root element <GlobalValueSet> <StandardValueSet>
Name source filename (dev name) <fullName> = fixed catalog name
Value element <customValue> <standardValue>
Can add NEW values? Yes No — modify existing only
Can create a new NAME? Yes No — only the fixed catalog
* wildcard in package.xml Supported Not supported

Specification

1. Purpose

This document defines the mandatory constraints for generating value-set metadata XML. The agent must verify these constraints before outputting XML to prevent Metadata API deployment errors.

  • GlobalValueSet — a reusable, named set of picklist values defined once and referenced by any number of picklist/multi-select fields. Use when the same value list is shared across multiple fields.
  • StandardValueSet — the value list behind a Salesforce-defined standard picklist (Industry, Lead Source, etc.). You can only modify the values in a fixed catalog of named sets; you cannot invent a new set or add brand-new values.

2. GlobalValueSet — Syntactic Essentials

File: globalValueSets/<DeveloperName>.globalValueSet-meta.xml

The developer name comes from the filename, not a <fullName> tag.

Required Elements

Element Requirement Notes
<masterLabel> Required UI label for the value set
<sorted> Required true = alphabetize values in the UI; false = preserve listed order
<customValue> Required (≥1) One per value (see below)

<customValue> Sub-Elements

Sub-element Requirement Notes
<fullName> Required The value's API name. Use the value text as the user spelled it — spaces are allowed and must be preserved (e.g. Closed Won, not Closed_Won). Must start with a letter. This is a value name, NOT a field API name, so do not append __c or replace spaces with underscores.
<default> Optional At most one value true. Omit it (or use false) on the rest — it is not required on every value.
<label> Required UI label for the value
<color> Optional Hex color, e.g. #FF0000
<isActive> Optional Omit (active) or false to deactivate
<description> Optional Per-value description

The __gvs Suffix — do NOT use it in metadata

Rule: reference a GlobalValueSet by its bare developer name. Never add __gvs.

In API 57.0+ orgs the platform stores/displays a GlobalValueSet's developer name with a __gvs suffix internally, but the Metadata API (deploy and retrieve) always uses the bare name<valueSetName>Priority_Levels</valueSetName>, not Priority_Levels__gvs. The suffix was briefly emitted by a Winter '23 change that caused deploy failures and was patched out. So:

  • The file is globalValueSets/Priority_Levels.globalValueSet-meta.xmlno __gvs in the filename.
  • A field references it as <valueSetName>Priority_Levels</valueSetName>no __gvs.
  • If a retrieve shows Priority_Levels__gvs in the org or you see a "returned from org but not found in local project" warning, that's the expected org-storage display — keep your local metadata on the bare name.

CORRECT — GlobalValueSet

<?xml version="1.0" encoding="UTF-8"?>
<GlobalValueSet xmlns="http://soap.sforce.com/2006/04/metadata">
    <masterLabel>Priority Levels</masterLabel>
    <sorted>false</sorted>
    <customValue>
        <fullName>Critical</fullName>
        <default>false</default>
        <label>Critical</label>
    </customValue>
    <customValue>
        <fullName>High</fullName>
        <default>false</default>
        <label>High</label>
    </customValue>
    <customValue>
        <fullName>Medium</fullName>
        <default>true</default>
        <label>Medium</label>
    </customValue>
    <customValue>
        <fullName>Low</fullName>
        <default>false</default>
        <label>Low</label>
    </customValue>
</GlobalValueSet>

INCORRECT — GlobalValueSet

<GlobalValueSet xmlns="http://soap.sforce.com/2006/04/metadata">
    <fullName>Priority_Levels</fullName>      <!-- WRONG: name comes from the filename -->
    <masterLabel>Priority Levels</masterLabel>
    <!-- WRONG: <sorted> is required and missing -->
    <standardValue>                            <!-- WRONG: GVS uses <customValue>, not <standardValue> -->
        <fullName>Critical</fullName>
    </standardValue>
</GlobalValueSet>

Errors: missing required sorted; unknown element standardValue; root fullName is rejected because the name is derived from the filename.


3. StandardValueSet — Syntactic Essentials CRITICAL

File: standardValueSets/<Name>.standardValueSet-meta.xml

HARD CONSTRAINTS — read before generating

  1. You can ONLY modify values inside the fixed catalog of named standard value sets. You cannot add a brand-new value, and you cannot create a new StandardValueSet name. The Metadata API will reject both.
  2. The root carries a <fullName> whose value is the fixed enum name (e.g. Industry), NOT a masterLabel. The filename must match this name.
  3. Values are <standardValue> entries — not <customValue>.
  4. Emit ONLY the values the request explicitly names — a surgical, minimal change. Include a <standardValue> block for each value the user asks you to activate, deactivate, relabel, or reorder, and nothing else. Do not enumerate the full picklist or emit <standardValue> entries for values the request did not mention. A StandardValueSet deployment is a partial update: unlisted values keep their current org state untouched. Reproducing every value (e.g. all 30+ Industry entries) is noise and risks clobbering org state — it is wrong even when the request says "keep only X active," which means "set the named ones; leave the rest as-is," not "enumerate and deactivate everything else." If you use the grounding MCP to discover existing values, use it only to confirm the named values exist and to get their exact <fullName>/<label> — not as a list to reproduce in full.

<standardValue> — Modifiable Sub-Elements

Sub-element Modifiable? Notes
<fullName> Identifies the value (must already exist) Cannot introduce a new one
<label> Yes Relabel the value's UI text
<isActive> Yes false deactivates; omit or true keeps active
<default> Yes At most one value true
<groupingString> Yes Category grouping (used by some standard picklists)

Canonical StandardValueSet Names (partial)

Industry, LeadSource, OpportunityStage, OpportunityType, AccountType, AccountRating, LeadStatus, CaseStatus, CaseOrigin, CasePriority, CaseReason, TaskStatus, TaskPriority, QuoteStatus, Product2Family, Salutation, AccountOwnership, ContractStatus, OrderStatus, PartnerRole.

Full appendix: the complete list of valid standard value set names is at https://developer.salesforce.com/docs/atlas.en-us.api_meta.meta/api_meta/standardvalueset_names.htm. If the name is not in that appendix, it is not a StandardValueSet — it is either a GlobalValueSet or an inline CustomField picklist.

CORRECT — StandardValueSet (modify existing values only)

<?xml version="1.0" encoding="UTF-8"?>
<StandardValueSet xmlns="http://soap.sforce.com/2006/04/metadata">
    <fullName>Industry</fullName>
    <standardValue>
        <fullName>Technology</fullName>
        <default>false</default>
        <label>Technology</label>
        <isActive>true</isActive>
    </standardValue>
    <standardValue>
        <fullName>Agriculture</fullName>
        <default>false</default>
        <label>Agriculture</label>
        <isActive>false</isActive>   <!-- deactivated, but kept in the set -->
    </standardValue>
</StandardValueSet>

INCORRECT — StandardValueSet

<StandardValueSet xmlns="http://soap.sforce.com/2006/04/metadata">
    <masterLabel>Industry</masterLabel>     <!-- WRONG: StandardValueSet uses <fullName>, not masterLabel -->
    <customValue>                           <!-- WRONG: uses <standardValue>, not customValue -->
        <fullName>Renewable Energy</fullName> <!-- WRONG: cannot ADD a new value to a standard set -->
        <label>Renewable Energy</label>
    </customValue>
</StandardValueSet>

Errors: unknown element masterLabel/customValue; adding a value not already in the standard catalog fails deployment.


4. Never Invent Values — Verify, Don't Hallucinate CRITICAL

When customizing a StandardValueSet (or extending a shared GlobalValueSet), only modify values that already exist — never invent the value list of a standard picklist. The hard rule is about what you EMIT: a <standardValue> whose <fullName> is not a real catalog value will fail deployment.

For well-known standard picklists you already know the canonical values (e.g. Industry, LeadSource, OpportunityStage). When you are unsure a named value exists, you can confirm it against the live org — but treat lookup as a confirmation step, not a required first call:

  • Grounding MCP (if available) exposes search_metadata and query-metadata to look up live metadata. Use them only to confirm a named value's exact <fullName>/<label> — not to pull the full list to reproduce.
  • CLI fallback — query the Tooling API directly:
sf data query --use-tooling-api \
  --query "SELECT MasterLabel, Metadata FROM StandardValueSet WHERE MasterLabel = '<name>'"

The point is the output, not the lookup: emit modifications ONLY to values you know exist. A generated StandardValueSet that introduces unseen values is a hallucination and will fail deployment. (This pairs with the minimal-scope rule in §3: confirm the named values; don't enumerate the whole set.)


5. Referencing a Value Set from a CustomField

A picklist/multi-select CustomField references a value set via <valueSetName> inside <valueSet> (instead of an inline <valueSetDefinition>).

<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
    <fullName>Priority__c</fullName>
    <label>Priority</label>
    <type>Picklist</type>
    <valueSet>
        <restricted>true</restricted>
        <valueSetName>Priority_Levels</valueSetName>  <!-- bare developer name, NO __gvs; see §2 -->
    </valueSet>
</CustomField>
  • For a GlobalValueSet, <valueSetName> is the bare developer name (e.g. Priority_Levels) — never add __gvs. The suffix is an org-storage display artifact; the Metadata API uses the bare name for both deploy and retrieve (see §2).
  • A field bound to a value set must not also declare an inline <valueSetDefinition> — choose one or the other.

6. Validation Rules

The agent must reject and explain — not silently "fix" by inventing metadata — the following:

Violation Action / Message
Add a NEW value to a StandardValueSet Reject. "Standard value sets cannot accept new values. Create a GlobalValueSet (reusable) or an inline picklist on a CustomField instead."
Create a NEW StandardValueSet name Reject. The name must be in the standard catalog appendix. Otherwise it is a GlobalValueSet.
Value set developer name with spaces / invalid chars Convert spaces to underscores; must start with a letter; alphanumeric + underscore only. Priority LevelsPriority_Levels. (This applies to the value SET name and the field API name — NOT to individual <customValue><fullName> values, which keep spaces as written.)
Duplicate value fullName within one set Reject. Each fullName must be unique within the value set.
More than one <default>true</default> Reject. At most one default value per set.

INCORRECT — adding a value to a standard set

"Add a Cryptocurrency value to the Industry picklist."

Do not emit a <standardValue> with fullName Cryptocurrency. Respond that standard value sets are a fixed catalog and propose a GlobalValueSet (if reused across fields) or an inline restricted picklist on a single CustomField.


7. Deployment Ordering

A value set must deploy before any CustomField that references it.

  • Deploy the GlobalValueSet / StandardValueSet first, then the CustomField whose <valueSetName> points at it.
  • A field referencing a value set that does not yet exist fails with valueSetName ... does not exist (or a "not found" error).
  • In package.xml: GlobalValueSet supports the * wildcard; StandardValueSet does not — list each standard set member explicitly.

8. Common Deployment Errors

Error / Symptom Cause Fix
Value not added to standard picklist Tried to ADD a value to a StandardValueSet Standard sets are fixed; use GlobalValueSet or inline CustomField picklist
Required field missing: sorted GlobalValueSet missing <sorted> Add <sorted>true</sorted> or <sorted>false</sorted>
Unknown element masterLabel (StandardValueSet) Used masterLabel instead of fullName StandardValueSet root uses <fullName> = catalog name
Unknown element customValue (StandardValueSet) Used customValue instead of standardValue Use <standardValue> in standard sets
valueSetName ... does not exist Field deployed before its value set, or __gvs wrongly added to the reference Deploy the value set first; reference it by the bare developer name with NO __gvs (§2)
Duplicate value name Two <customValue> entries share a fullName Make each fullName unique within the set (spaces in a value name are allowed — do NOT underscore them)

Verification Checklist

Before generating value-set XML, verify:

Type Selection

  • Is this a reusable set shared across fields (GlobalValueSet) or a built-in standard picklist (StandardValueSet)?
  • If StandardValueSet: is the name in the standard catalog appendix? If not, it must be a GlobalValueSet or inline picklist.

GlobalValueSet Checks

  • Is the root <GlobalValueSet> with namespace http://soap.sforce.com/2006/04/metadata?
  • Is <masterLabel> present?
  • Is <sorted> present (true or false)?
  • Is there at least one <customValue>, each with <fullName> and <label> (at most one carrying <default>true</default>)?
  • Is there NO root <fullName> (name comes from the filename)?
  • When referencing from a field, is <valueSetName> the bare developer name with NO __gvs suffix?

StandardValueSet Checks CRITICAL

  • Are you emitting modifications ONLY to values you know exist (confirmed from known standard catalogs, or via grounding search_metadata/query-metadata / Tooling API if unsure) — never invented values?
  • Is the root <StandardValueSet> with the correct namespace?
  • Does the root use <fullName> set to the fixed catalog name (NOT masterLabel)?
  • Are values <standardValue> entries (NOT customValue)?
  • Are you ONLY modifying values that already exist (no new fullName)?
  • Did you avoid adding a brand-new value or a new set name?

Shared Checks

  • At most one value has <default>true</default>?
  • Are all value fullNames unique within the set? (Spaces in a value name are fine — preserve them as written; only the value-SET developer name and field API name use underscores.)
  • Does the value set deploy BEFORE any CustomField that references it?
  • Does the filename match the intended name?

trang chủ - Wiki
Copyright © 2011-2026 iteam. Current version is 2.155.2. UTC+08:00, 2026-07-09 03:50
浙ICP备14020137号-1 $bản đồ khách truy cập$