salesforce-validation-rule-writer-skill
Pure-generation T0 skill that translates plain-English business rules into production-ready Salesforce validation rule formula syntax. Outputs the formula, a clear error message, profile bypass guidance, null handling, and a formula compilation checklist. Nothing is deployed — this skill generates text only.
When This Skill Owns the Task
Use salesforce-validation-rule-writer-skill when the request is to write or design a validation rule formula:
- "Write a validation rule that requires Billing Country when the account type is Partner"
- "Block reps from saving an Opportunity with Stage = Closed Won if Amount is blank"
- "Create a formula to enforce that Close Date cannot be in the past on new records"
- "Require phone number when Lead Source is Outbound"
- "Prevent picklist selection of Status = Inactive if there are open Cases"
Delegate elsewhere when:
| Situation | Skill to use | |---|---| | Reviewing or auditing existing validation rule logic | salesforce-platform-admin-review-agent | | Deploying the generated formula to an org | salesforce-deployment-validator-skill | | Bulk enabling/disabling many rules across objects | salesforce-bulk-data-ops-skill | | Need to check what existing rules are on an object | salesforce-metadata-fetcher-skill |
Required Context to Gather First
Before generating a formula, confirm:
- Target object — standard (Account, Contact, Opportunity, Lead, Case)
or custom (My_Object__c).
- Field API names — the exact API names of every field referenced in the
rule. Ask if only labels are provided.
- Trigger condition — precisely when should the save be blocked? Describe
the AND/OR logic in English.
- Record types in scope — does the rule apply to all record types or only
specific ones? (Affects RecordType.DeveloperName branching.)
- New vs. edit context — should the rule fire on new records only, edits
only, or both? (ISNEW, ISCHANGED implications.)
- Profile bypass requirement — which profiles (if any) should be exempt?
(System Admin bypass is almost always required.)
- Error message placement — field-level error or page-level error?
- Picklist field types — if picklist fields are used, confirm the exact
API values (not labels) of the controlling values.
If any critical context is missing, ask before generating.
Recommended Workflow
Step 1 — Confirm and rephrase the business rule
Restate the rule in your own words to confirm understanding:
"This rule fires when: [condition]. It blocks save with: [error text].
It is bypassed for: [profile/record type]. It applies on: [new/edit/both]."
Ask for confirmation before proceeding.
Step 2 — Decompose into atomic conditions
Break the rule into atomic boolean conditions. Map each condition to the appropriate Salesforce formula function:
| Condition type | Formula pattern | |---|---| | Field is blank | ISBLANK(Field__c) for text; ISNULL(Field__c) for number/date | | Field equals picklist value | TEXT(Field__c) = "API_Value" | | Field changed | ISCHANGED(Field__c) | | New record | ISNEW | | Prior value comparison | PRIORVALUE(Field__c) | | Record type check | RecordType.DeveloperName = "RecordTypeName" | | Date in past | Field__c < TODAY |
Step 3 — Compose the formula with AND/OR/NOT
Wrap atomic conditions using:
AND(cond1, cond2)— both must be trueOR(cond1, cond2)— either must be trueNOT(cond)— negation
Prefer explicit AND/OR over &&/|| operators for readability and tooling compatibility. Nest carefully — Salesforce formulas have a 5,000-character compiled size limit.
Step 4 — Add profile bypass
Unless the customer explicitly opts out, wrap the core logic with a System Administrator bypass:
AND(
$Profile.Name <> "System Administrator",
<your_core_formula>
)
For additional profiles, use:
AND(
NOT(OR(
$Profile.Name = "System Administrator",
$Profile.Name = "Sales Ops"
)),
<your_core_formula>
)
Step 5 — Apply null handling
Validate that every text, date, and number field access guards against null where needed:
- Text fields: use
ISBLANKnot= "" - Number/currency fields: use
ISNULLorBLANKVALUE(Field__c, 0)before
arithmetic
- Date fields: use
ISNULLor compare after null guard - Picklist fields: use
TEXT(Field__c) = "Value"notField__c = "Value" - Multi-select picklists: use
INCLUDES(Field__c, "Value")
Step 6 — Write the error message
Apply these principles (see references/error-message-style.md):
- 15 words or fewer for the primary instruction
- Start with what the user must do, not what they did wrong
- Name the specific field or value required
- Include the condition context when space allows
- No jargon: avoid "null", "API name", "formula"
Good: "Close Date cannot be in the past. Set a future date to save." Bad: "Validation error: CloseDate field value failed validation formula."
Step 7 — Compile safety check
Before emitting, verify mentally:
- [ ] Formula evaluates to TRUE when the rule should block (not the inverse)
- [ ] No division by zero if arithmetic is used (use
IF(divisor = 0, ...)) - [ ] PRIORVALUE only used on edits (wrap with
NOT(ISNEW)) - [ ] ISCHANGED only used on edits (same)
- [ ] All referenced fields exist on the stated object
- [ ] No cross-object formula deeper than 10 relationship hops
- [ ] No circular reference risk (field does not reference itself)
- [ ] Picklist values use TEXT wrapper
Step 8 — Emit structured output
Produce the full output block per the Output Format section below.
Quality Scoring Rubric (100-point)
Score every output before emitting. Threshold: 85+ ship, 70–84 ship with caveat, below 70 reject and revise.
| Dimension | Points | What earns full marks | |---|---|---| | Formula correctness | 30 | Formula evaluates to TRUE when it should block; correct operators; picklist TEXT wrapper; ISBLANK/ISNULL appropriately placed | | Error message clarity | 20 | ≤ 15 words, action-oriented, names the required field or condition, no technical jargon | | Profile bypass awareness | 15 | System Admin bypass present unless explicitly opted out; additional profiles listed; bypass wrapper syntactically correct | | Null handling | 15 | Every field access null-guarded; no runtime errors on blank fields; BLANKVALUE used where arithmetic is involved | | Governor limit safety | 10 | Compiled size < 5,000 chars; no cross-object formula chain deeper than 5 hops; no expensive SOQL-like patterns in formulas | | Formula compilation safety | 10 | PRIORVALUE/ISCHANGED gated by NOT(ISNEW); no division by zero; no circular references; all functions used with correct argument counts |
Scoring penalties:
- Formula evaluates to FALSE when it should block (inverted logic): -30
- Missing null guard on a field that can be blank in the target object: -10 per field (max -20)
- Missing System Admin bypass when not explicitly opted out: -10
- Error message exceeds 30 words: -10
- PRIORVALUE used without ISNEW guard: -15
T0 Contract
This skill is static-review (T0) only:
- No Salesforce org connection required or permitted.
- No CLI commands are executed.
- No metadata is fetched from a live org.
- Output is a text formula and supporting notes only.
- The user must copy the formula into Setup → Object Manager → Validation Rules
or deploy it via salesforce-deployment-validator-skill.
Refusal Triggers
Stop and decline if:
- The request is to review or audit an existing formula (route to
salesforce-platform-admin-review-agent).
- The request is to deploy the formula to a live org (route to
salesforce-deployment-validator-skill).
- The formula requested would bypass all validation for all profiles
permanently (security risk — flag and decline).
- The target object cannot be determined afte