Help us improve
Share bugs, ideas, or general feedback.
From sf-demo-scout
Generates and validates Salesforce Custom Field metadata with mandatory constraints to prevent deployment errors, focusing on Roll-up Summary, Master-Detail, and formula fields.
npx claudepluginhub seb-schi/sf-demo-scout --plugin sf-demo-scoutHow this skill is triggered — by the user, by Claude, or both
Slash command
/sf-demo-scout:generating-custom-fieldThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Use this skill when you need to:
Generates and validates Salesforce Custom Field metadata with mandatory constraints to prevent deployment errors, focusing on Roll-up Summary, Master-Detail, and formula fields.
Translates plain-English business rules into deployable Salesforce validation rule formula syntax, including error messages, profile bypass, null handling, and formula safety checks.
Defines and evolves Dataverse data models — tables, columns, relationships, forms, and views — using the Python SDK and Web API within a solution context. Publishes metadata changes to the environment rather than hand-editing XML.
Share bugs, ideas, or general feedback.
Use this skill when you need to:
Generate and validate Salesforce Custom Field metadata with mandatory constraints to prevent deployment errors. This skill has special focus on the highest-failure-rate field types: Roll-up Summary and Master-Detail relationships.
This document defines the mandatory constraints for generating CustomField metadata XML. The agent must verify these constraints before outputting XML to prevent Metadata API deployment errors.
Critical Focus Areas:
Every generated field must include these tags:
| Attribute | Requirement | Notes |
|---|---|---|
<fullName> | Required | Derive from <label>: capitalize each word, replace spaces with _, append __c. Must start with a letter. E.g., label Total Contract Value → Total_Contract_Value__c |
<label> | Required | The UI name (Title Case) |
<description> | Mandatory | State the business "why" behind the field |
<inlineHelpText> | Mandatory | Provide actionable guidance for the end-user. Must add value beyond the label (e.g., "Enter the value in USD including tax" instead of just "The amount") |
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
To ensure deployment success, follow these mathematical constraints:
precision is the total digits; scale is the decimal digitsprecision ≤ 18 AND scale ≤ precisionprecision - scaleFor standard TextArea types, the Metadata API requires <length>255</length>, even though it isn't configurable in the UI.
Mandatory for Long/Rich text and Multi-select picklists to control UI height.
| 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 | 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 with valueSetDefinition and restricted |
| 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 |
| Type | <type> Value | Required Attributes |
|---|---|---|
| Formula | Result type (e.g., Number) | formula, formulaTreatBlanksAs |
| Roll-Up Summary | Summary | See Section 6 for complete requirements |
| Multi-Select Picklist | MultiselectPicklist | valueSet, visibleLines (default 4) |
| Type | <type> Value | Required Attributes |
|---|---|---|
| Geolocation | Location | scale, displayLocationInDecimal |
restricted RuleThe <restricted> boolean inside <valueSet> controls whether only admin-defined values are allowed.
<restricted>true</restricted> (restricted, avoids performance issues with large picklist value sets)<restricted>false</restricted><valueSet>
<restricted>true</restricted>
<valueSetDefinition>
<sorted>false</sorted>
<value>
<fullName>Option_A</fullName>
<default>false</default>
<label>Option A</label>
</value>
</valueSetDefinition>
</valueSet>
Master-Detail fields have strict attribute restrictions that differ from Lookup fields. Violating these rules causes deployment failures.
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 |
| 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 |
<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
<fullName>Account__c</fullName>
<label>Account</label>
<type>MasterDetail</type>
<referenceTo>Account</referenceTo>
<relationshipName>Contacts</relationshipName>
<relationshipOrder>0</relationshipOrder>
<required>true</required> <!-- WRONG: Remove this -->
<deleteConstraint>Cascade</deleteConstraint> <!-- WRONG: Remove this -->
<lookupFilter> <!-- WRONG: Remove this entire block -->
<active>true</active>
<filterItems>
<field>Account.Type</field>
<operation>equals</operation>
<value>Customer</value>
</filterItems>
</lookupFilter>
</CustomField>
Errors:
Master-Detail Relationship Fields Cannot be Optional or RequiredCan not specify 'deleteConstraint' for a CustomField of type MasterDetailLookup filters are only supported on Lookup Relationship Fields<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>
<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>
0, second = 1Travel_Bookings)Roll-up Summary fields have the highest deployment failure rate. Follow these rules exactly.
| 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 |
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 |
CRITICAL: Both summaryForeignKey and summarizedField MUST use the fully qualified format:
ChildObjectAPIName__c.FieldAPIName__c
Decision Logic:
summaryForeignKey = ChildObject__c.MasterDetailFieldOnChild__csummarizedField = ChildObject__c.FieldToSummarize__c<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 SummaryMust specify the name in the CustomObject.CustomField format (e.g. Account.MyNewCustomField)<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>
<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
<fullName>Line_Item_Count__c</fullName>
<label>Line Item Count</label>
<description>Count of related line items</description>
<inlineHelpText>Automatically calculated from child records</inlineHelpText>
<type>Summary</type>
<summaryOperation>count</summaryOperation>
<summaryForeignKey>Order_Line_Item__c.Order__c</summaryForeignKey>
<!-- NO summarizedField needed for COUNT -->
<!-- NO precision, scale, required, or length -->
</CustomField>
<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
<fullName>Earliest_Due_Date__c</fullName>
<label>Earliest Due Date</label>
<description>Earliest due date among all line items</description>
<inlineHelpText>Shows the soonest deadline</inlineHelpText>
<type>Summary</type>
<summaryOperation>min</summaryOperation>
<summarizedField>Order_Line_Item__c.Due_Date__c</summarizedField>
<summaryForeignKey>Order_Line_Item__c.Order__c</summaryForeignKey>
</CustomField>
<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
<fullName>Highest_Price__c</fullName>
<label>Highest Price</label>
<description>Maximum unit price among all line items</description>
<inlineHelpText>Shows the most expensive item</inlineHelpText>
<type>Summary</type>
<summaryOperation>max</summaryOperation>
<summarizedField>Order_Line_Item__c.Unit_Price__c</summarizedField>
<summaryForeignKey>Order_Line_Item__c.Order__c</summaryForeignKey>
</CustomField>
| 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 |
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> tag MUST be wrapped in a <![CDATA[ ... ]]> section. This prevents the XML parser from interpreting formula operators (like &, <, >) as XML markup.]]>, escape it by breaking the CDATA block: e.g., <![CDATA[Text_Field__c & "]]]]><![CDATA[>"]]>returnType. This does not exist in the Metadata API. The <type> tag defines the return data type of the formula result.Decision Logic:
Number, Currency, or Percent → set <formulaTreatBlanksAs>BlankAsZero</formulaTreatBlanksAs>Text, Date, or DateTime → set <formulaTreatBlanksAs>BlankAsBlank</formulaTreatBlanksAs><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>
<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 fields that reference other fields will fail deployment if the referenced field does not exist or has not been deployed yet. Ensure all referenced fields are deployed before the formula field.
| Function | Rule |
|---|---|
TEXT() | MUST NOT be used with Text fields. If the field is already Text, remove the TEXT() wrapper. |
CASE() | Last parameter is always the default value. Total parameter count MUST be even (value-result pairs + default). |
VALUE() | MUST only be used with Text fields. If a Number is passed as parameter, remove the VALUE() wrapper. |
DAY() | MUST only be used with Date fields. If a DateTime field is used, convert it to Date first (e.g., DAY(DATEVALUE(DateTimeField__c))). |
MONTH() | MUST only be used with Date fields. If a DateTime field is used, convert it to Date first (e.g., MONTH(DATEVALUE(DateTimeField__c))). |
DATEVALUE() | MUST only be used with DateTime fields. If a Date field is used, remove the DATEVALUE() wrapper. |
ISPICKVAL() | MUST be used when checking equality of a Picklist field. NEVER use == with Picklist fields. |
ISCHANGED() | Use ISCHANGED() to check if a field value has changed. Do not manually compare with PRIORVALUE(). |
| 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. |
Before generating CustomField XML, verify:
<fullName> use valid format and end in __c?<description> and <inlineHelpText> both populated and meaningful?<label> in Title Case?<!-- ... -->) before the root <CustomField> element? (Comments before the root element break SDR's parser)<required> attribute ABSENT? (Master-Detail is always required)<deleteConstraint> attribute ABSENT? (Master-Detail always cascades)<lookupFilter> block ABSENT? (Only for Lookup fields)<relationshipOrder> set to 0 or 1?<sharingModel> set to ControlledByParent?<deleteConstraint> set to SetNull, Restrict, or Cascade?<relationshipName> in plural PascalCase?<precision> attribute ABSENT?<scale> attribute ABSENT?<summaryForeignKey> in format ChildObject__c.MasterDetailField__c?<summarizedField> in format ChildObject__c.FieldName__c?<summarizedField> ABSENT?<type> set to result type (NOT "Formula")?<formula> content wrapped in <![CDATA[ ... ]]>?<returnType> attribute ABSENT? (does not exist in Metadata API)<formulaTreatBlanksAs> set to BlankAsZero for numeric results or BlankAsBlank for text/date results?scale ≤ precision?precision ≤ 18?<length>255</length> explicitly included?<visibleLines> set?Order, Group, Select, etc.)?