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 |
|---|
| Required | Field name only: derive from — capitalize each word, replace spaces with , append . Must start with a letter. E.g., label → . ⚠️ This rule is for the FIELD name. Picklist VALUE is different — keep it exactly as the user spelled it, spaces and all, no (e.g. , NOT ). See references/advanced-picklists.md
(ref §3). |
| Required | The UI name (Title Case) |
| Always include | Explain the business reason why this field exists. |
| Always include | Actionable end-user guidance that adds value beyond the label (e.g., "Enter the value in USD including tax", not "The amount"). |
and
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
is the object's API name (
,
, or a custom
). 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
- is the total digits; is the decimal digits
- Rule: AND
- Calculation: Digits to the left of decimal =
The "Fixed 255" Rule
TextArea: always include 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
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 | Value | Required Attributes |
|---|
| Auto Number | | (must include ), |
| Checkbox | | Default to |
| Date | | No precision/length required |
| Date/Time | | No precision/length required |
| Email | | Built-in format validation |
| Lookup Relationship | | , , |
| Master-Detail Relationship | | , , |
| Number | | , |
| Currency | | Default precision: 18, scale: 2 |
| Percent | | Default precision: 5, scale: 2 |
| Phone | | Standardizes phone number formatting |
| Picklist | | containing EITHER (inline) OR (reference); (see "Picklist default" below; advanced cases in §3.4) |
| Text | | (Max 255) |
| Text Area | | |
| Text (Long) | | , (default 3) |
| Text (Rich) | | , (default 25) |
| Time | | Stores time only (no date) |
| URL | | Validates for protocol and format |
3.2 Computed & Multi-Value Types
| Type | Value | Required Attributes |
|---|
| Formula | Result type (e.g., ) | , |
| Roll-Up Summary | | See Section 5 for complete requirements |
| Multi-Select Picklist | | , (default 4) |
3.3 Specialized Types
| Type | Value | Required Attributes |
|---|
| Geolocation | | , |
Picklist default
Always set <restricted>true</restricted>
inside
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:
xml
<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
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 holds EITHER (reference) OR
(inline) — never both. Reference by the bare developer name —
Standard set , GlobalValueSet with NO and no
(the 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 / keep the user's exact
text including spaces (, never ). The space→ + rule is for
the FIELD name only.
- Dependent picklists (ref §2). Use the modern API 38.0+ form: +
one (+) per pair; never the legacy
// tags. Both controlling and
dependent fields MUST be
<restricted>true</restricted>
, even if the request doesn't say so.
- Enhanced value attributes (ref §3). entries also accept (hex, leading
), ( retires a value), and a value-level .
- Scoping a picklist to a record type (ref §5). Per-record-type value visibility lives on the
RecordType (), not the field. The RecordType file carries its own
(bare developer name). First decide if the object needs a BusinessProcess:
only Opportunity / Lead / Case / Solution require one — they won't deploy without a
(
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 (after
, before ; the in the BP file is bare, never
object-qualified). Custom objects () 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 |
|---|
| Master-Detail is ALWAYS required by design | Deployment error |
| Master-Detail ALWAYS cascades deletes | Deployment error |
| Only supported on Lookup fields | Deployment error |
Master-Detail vs Lookup Comparison
| Attribute | Master-Detail | Lookup |
|---|
| ❌ FORBIDDEN | ✅ Optional |
| ❌ FORBIDDEN (always CASCADE) | ✅ Required (, , ) |
| ❌ FORBIDDEN | ✅ Optional |
| ✅ Required (0 or 1) | ❌ Not applicable |
<reparentableMasterDetail>
| ✅ Optional | ❌ Not applicable |
<writeRequiresMasterRead>
| ✅ Optional | ❌ Not applicable |
INCORRECT — Master-Detail with forbidden attributes:
xml
<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:
xml
<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):
xml
<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 = , second =
- Relationship Name: Must be a plural PascalCase string (e.g., )
- 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 |
|---|
| Required | Always |
| Required | , , , or |
| Required | ChildObject__c.MasterDetailField__c
|
| Conditional | Required for , , . NOT for |
Forbidden Elements on Roll-Up Summary
NEVER include these attributes on Roll-Up Summary fields:
| Forbidden Attribute | Why |
|---|
| Summary inherits from summarized field |
| Summary inherits from summarized field |
| Not applicable to Summary fields |
| Not applicable to Summary fields |
Format Rules for summaryForeignKey and summarizedField
CRITICAL: Both
and
MUST use the fully qualified format:
text
ChildObjectAPIName__c.FieldAPIName__c
Decision Logic:
- =
ChildObject__c.MasterDetailFieldOnChild__c
- =
ChildObject__c.FieldToSummarize__c
INCORRECT — Roll-Up Summary with common errors:
xml
<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):
xml
<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 entirely (and keep
).
MIN / MAX: identical to SUM — just
<summaryOperation>min</summaryOperation>
or
, with
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 |
|---|
| NO | Count number of child records |
| YES | Add up numeric values |
| YES | Find smallest value |
| 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
tag is added to a field whose
is set to the
result data type:
Formula XML Generation Rules
- The contents of the tag MUST be wrapped in a 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 . This does not exist in the Metadata API. The tag defines the return data type of the formula result.
formulaTreatBlanksAs Rule
Decision Logic:
- IF formula result type = , , or → set
<formulaTreatBlanksAs>BlankAsZero</formulaTreatBlanksAs>
- IF formula result type = , , or → set
<formulaTreatBlanksAs>BlankAsBlank</formulaTreatBlanksAs>
INCORRECT — Using Formula as type:
xml
<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:
xml
<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 (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 element | Remove XML comments () that appear before in the 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 |
| 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 , , etc. | Rename to , etc. |
Value set must reference a value set name or define a value set, but not both
| has both and | Keep exactly one (see Section 3.4) |
duplicate value found: [X] is defined multiple times
| Two entries share a | Make every picklist value unique |
| on a picklist value | Value 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 (//) | Use the modern // form (Section 3.4) |
8. Verification Checklist
Before generating CustomField XML, verify:
Universal Checks
Master-Detail Field Checks CRITICAL
Lookup Field Checks
Picklist Field Checks
Roll-Up Summary Field Checks CRITICAL
Formula Field Checks
Numeric Field Checks
Text Area Checks
Relationship Limit Checks
Naming Checks