platform-custom-setting-generate
Use this skill when users need to create, generate, or validate Salesforce Custom Setting metadata. Trigger when users mention custom settings (hierarchy or list), customSettingsType, SetupOwnerId, per-profile/per-user config overrides, feature flags, feature toggles, kill switches, or on/off switch
By forcedotcom · 675 installs
npx skills add forcedotcom/sf-skills --skill platform-custom-setting-generate
Source repository · Upstream listing
Salesforce Custom Setting Generator and Validator
When to Use This Skill
Use this skill when you need to:
Create a Hierarchy or List custom setting
Generate custom setting metadata XML
Add or validate fields on an existing custom setting
Populate custom setting values (these are data, not metadata — see Section 6)
Troubleshoot custom setting deployment errors
A trigger bypass / kill switch is a Custom Setting — not Apex and not a mdt . When the user wants a
switch admins can flip to turn behavior on or off — "disable my Account triggers during a data load", a
feature toggle, a maintenance mode flag — generate only a hierarchy custom setting with a Checkbox
field (e.g. Disable Triggers c / Bypass c ). Do not author the Apex trigger, handler, or test
that reads it, and do not model it as a Custom Metadata Type: the per profile/per user override a
bypass flag needs is exactly what a hierarchy custom setting gives you and a mdt does not. The Apex
that checks the flag is the developer's to write — this skill generates the setting only.
1. Overview and Purpose
This document defines the mandatory constraints for generating Custom Setting metadata. A custom setting is
a CustomObject with <customSettingsType set — it is not a distinct metadata type.
File extension: .object meta.xml
File path: force app/main/default/objects/<Name c/<Name c.object meta.xml
API name suffix: c (identical to a regular custom object — the suffix does not distinguish them)
Values are data, not metadata. You can generate the setting's definition as XML, but you cannot
deploy its values that way. There is no source format equivalent of customMetadata/ for custom
settings. Never generate a file that claims to carry setting values — see Section 6 for what to do
instead.
2. Syntactic Essentials (Tier 1)
<customSettingsType is mandatory — CRITICAL
This is the single highest severity rule in this skill. Omitting <customSettingsType does not
produce a "you forgot customSettingsType" error. The component silently stops being a custom setting and
is validated as a plain custom object.
The failure is dangerous because it is recoverable in the wrong direction : an agent that omits the
element, then obediently fixes each error the platform reports, ends up with a green deploy and
completely the wrong kind of component .
What you see What it means
Must specify a non empty plural label for the CustomObject You are NOT building a custom setting. customSettingsType is missing. Add it — do not add pluralLabel .
Cannot specify: nameField for CustomSettings You ARE building a custom setting. Remove the named element.
These two strings are mutually exclusive tells. The giveaway in the first is the phrase
for the CustomObject and the absence of any mention of custom settings.
If a deploy reports Must specify a non empty plural label for the CustomObject on something the user
asked to be a custom setting, never satisfy that error by adding <pluralLabel . Adding it (plus
nameField , deploymentStatus , and sharingModel ) makes the deploy succeed and creates a regular custom
object that the user did not ask for.
Required and Allowed Elements
Element Requirement Notes
<customSettingsType Required Hierarchy or List — see Section 3
<label Required Singular UI name
<visibility Always include Public , or Protected only in a dev/sandbox/scratch org (Section 5)
<description Always include Explain what the setting controls and who edits it
<enableFeeds Optional Accepted
<listViews Optional Accepted — permitted despite recordTypes and compactLayouts being forbidden
Forbidden Elements
Every element below produces Cannot specify: <element for CustomSettings :
Forbidden element Note
<pluralLabel Required on a regular custom object, forbidden here. Exactly inverted.
<nameField The Name field exists implicitly on List settings
<deploymentStatus
<sharingModel Custom settings are not shared records
<enableActivities <enableReports <enableHistory <enableSearch
<validationRules Enforce these in Apex instead
<recordTypes
<compactLayouts
INCORRECT — carries a regular custom object's required elements:
Errors: Cannot specify: pluralLabel for CustomSettings · Cannot specify: sharingModel for CustomSettings · Cannot specify: deploymentStatus for CustomSettings · Cannot specify: nameField for CustomSettings
CORRECT — minimum valid custom setting:
Note there is no <fullName . A root level one is tolerated and ignored, but omit it — the API name comes
from the directory and filename.
<customSettingsType List</customSettingsType deployed without needing the "Manage List Custom Settings
Type" toggle in Setup. That toggle exists in some orgs, so if a List setting is rejected on a
customSettingsType grounds in a different org, check Schema Settings before assuming the XML is wrong.
3. List vs Hierarchy Decision (Tier 2)
<customSettingsType has exactly two values, and the choice changes how rows are addressed.
Hierarchy List
Use when The value can vary per profile or per user, with an org wide fallback The setting is a small keyed reference table, the same for everyone
Row key SetupOwnerId (Organization, Profile, or User) Name
Resolution User value → Profile value → org default Look up by Name
Apex read MySetting c.getInstance() / getOrgDefaults() MySetting c.getValues('Key') / getAll()
Typical case Feature flags, per profile limits, debug toggles Country codes, tax rates by region, integration endpoints by key
Default to Hierarchy when the user describes toggles, limits, or anything that "can be overridden."
Choose List when they describe a lookup table with named rows.
If the user asks for a keyed reference table that should be deployable between orgs , a List custom
setting is usually the wrong answer — its rows are data and will not travel with the deploy. Route to
platform custom metadata type generate instead (Section 7).
4. Field Rules
Fields on a custom setting are ordinary CustomField components at
objects/<Name c/fields/<Field c.field meta.xml .
This skill owns the custom setting specific deltas only — the supported type allowlist and the
fieldManageability prohibition below. For everything generic ( <fullName derivation, <label ,
<description , <inlineHelpText , precision/scale, <length , externalId ), follow
platform custom field generate .
Supported Field Types
Checkbox , Currency , Date , DateTime , Email , Number , Percent , Phone , Text , TextArea , Url
required , unique , externalId , and defaultValue are accepted on custom setting fields. Whether a
given one applies to a given type is a generic field rule — defer to platform custom field generate .
Unsupported Field Types
Picklist , MultiselectPicklist , LongTextArea , Html , Lookup , MasterDetail , AutoNumber ,
Location , Time , EncryptedText , and Formula (a <formula element on any type).
Every one of these fails with the same bare, uninformative string:
This error names neither field nor type. On a multi field deploy, read componentFailures[].fullName
from the json output to find the culprit — do not guess.
Roll up summary fields are structurally impossible here — a Summary needs a master detail child, and
MasterDetail is itself rejected on a custom setting. Don't generate one; the error text varies, so don't
match on a specific string.
Picklist is the common trap. Users frequently ask for a picklist on a custom setting. It is not
supported. Use Text and enforce the allowed values in Apex, or route the request to
platform custom metadata type generate — CMDT does support Picklist.
Near inversion vs CMDT: Currency works here but not on CMDT; Picklist / LongTextArea work on CMDT but
not here. Never carry field type assumptions across the two families.
<fieldManageability is forbidden
That element belongs to CMDT fields only. On a custom setting field it fails with:
5. Visibility, Secrets, and the No Silent Downgrade Rule
Protected is org type dependent
<visibility Protected</visibility deploys only in a developer, sandbox, or scratch org. Anywhere else:
The no silent downgrade rule — CRITICAL
If Protected fails because of the org type, never "fix" it by switching to Public . A silent
downgrade turns a deliberate confidentiality choice into a world readable component with no signal. Report
it plainly: the org does not permit Protected , so the options are a dev/sandbox/scratch org or accepting
Public — let the user decide. This applies to any visibility narrowing, not just this error.
Secrets do not belong in a custom setting
Custom settings are not a secret store. Values are readable by anyone who can query the object, and
Protected does not change that for code in the same namespace.
When a user asks to store an API key, password, token, client secret, or certificate in a custom setting:
1. Warn prominently and first — before generating anything — that a custom setting is the wrong place
for a credential and the value will be readable.
2. Recommend the right component — a Named Credential with an External Credential for callout
auth. Say plainly that this skill does not generate those.
3. If the user still insists, comply — generate the setting but keep the warning in the response. Do not
silently refuse, and do not silently obey.
Never route a secret to Protected visibility as a compromise: if Protected is unavailable (above),
the no silent downgrade rule applies with full force, because the downgrade would publish the secret.
6. Setting Values Are Data, Not Metadata
A custom setting's rows cannot be deployed as XML. There is no customMetadata/ style folder for them.
Only the object definition and its fields are metadata.
When a user asks to create a setting with values — "add a feature flag setting with Beta enabled for
admins" — do all of the following:
1. Generate the object and field XML as normal.
2. State the limitation in one line: setting values are data, not metadata, so they are not deployed.
3. Give ready to run commands inline in the chat response , fully substituted with the real object and
field API names and the actual values — not a placeholder template, and not written to a file.
All three shapes below are verified working.
Hierarchy — org wide default row
Omit SetupOwnerId . It defaults to the Organization Id, which is exactly the org default row. No Id
lookup is needed.
Hierarchy — profile or user override
This one does need an Id, so it is two steps. A user level override uses the same shape with a User Id
in place of the Profile Id (extrapolated from the profile case, not separately verified).
An override row coexists with the org default row; it does not replace it.
List — one row per key
Name is the row key and is required.
Quoting
Wrap the whole values argument in double quotes; single quote any value with a space or shell special
character ( Environment Label c='org default' ). Plain numbers, booleans, and Ids need no inner quotes.
Consuming a setting in Apex
Values are read through the generated typed class — cached, so reads cost no SOQL and are safe inside
loops and triggers. For a Hierarchy setting:
getInstance() and getOrgDefaults() never return null (API ≥ 22) — a missing record comes back as an
empty row, so test org.Id != null when you need to know whether a real record actually exists. For a
List setting, use getValues('Key') for one row or getAll()