Creates and validates Salesforce flows with 110-point scoring and Winter '26 best practices...
Expert Salesforce Flow Builder with deep knowledge of best practices, bulkification, and Winter '26 (API 65.0) metadata. Create production-ready, performant, secure, and maintainable flows.
Validate Flow XML before deployment:
# Path to validation script
python3 ~/.claude/plugins/marketplaces/sf-skills/sf-flow-builder/hooks/scripts/validate_flow.py <flow-file.xml>
# Example
python3 ~/.claude/plugins/marketplaces/sf-skills/sf-flow-builder/hooks/scripts/validate_flow.py \
force-app/main/default/flows/Auto_Lead_Assignment.flow-meta.xml
Scoring: 110 points across 6 categories. Minimum 88 (80%) for deployment.
sf-metadata → sf-flow → sf-deploy → sf-data (you are here: sf-flow)
⚠️ Flow references custom object/fields? Create with sf-metadata FIRST. Deploy objects BEFORE flows.
1. sf-metadata → Create objects/fields (local)
2. sf-flow ◀── YOU ARE HERE (create flow locally)
3. sf-deploy → Deploy all metadata (remote)
4. sf-data → Create test data (remote - objects must exist!)
See docs/orchestration.md for extended orchestration patterns including Agentforce.
| Insight | Details |
|---|---|
| Before vs After Save | Before-Save: same-record updates (no DML), validation. After-Save: related records, emails, callouts |
| Test with 251 | Batch boundary at 200. Test 251+ records for governor limits, N+1 patterns, bulk safety |
| $Record context | Single-record, NOT a collection. Platform handles batching. Never loop over $Record |
| Transform vs Loop | Transform: data mapping/shaping (30-50% faster). Loop: per-record decisions, counters, varying logic. See docs/transform-vs-loop-guide.md |
Before building, evaluate alternatives: See docs/flow-best-practices.md Section 1 "When NOT to Use Flow" - sometimes a Formula Field, Validation Rule, or Roll-Up Summary Field is the better choice.
Use AskUserQuestion to gather:
Pre-Development Planning: For complex flows, document requirements and sketch logic before building. See docs/flow-best-practices.md Section 2 "Pre-Development Planning" for templates and recommended tools.
Then:
Glob: pattern="**/*.flow-meta.xml"docs/subflow-library.md (in sf-flow folder)docs/governance-checklist.md (in sf-flow folder)Select template:
| Flow Type | Template File |
|---|---|
| Screen | screen-flow-template.xml |
| Record-Triggered | record-triggered-*.xml |
| Platform Event | platform-event-flow-template.xml |
| Autolaunched | autolaunched-flow-template.xml |
| Scheduled | scheduled-flow-template.xml |
| Wait Elements | wait-template.xml |
Element Pattern Templates (templates/elements/):
| Element | Template | Purpose |
|---|---|---|
| Loop | loop-pattern.xml |
Complete loop with nextValueConnector/noMoreValuesConnector |
| Get Records | get-records-pattern.xml |
All recordLookups options (filters, sort, limit) |
| Delete Records | record-delete-pattern.xml |
Filter-based and reference-based delete patterns |
Template Path Resolution (try in order):
~/.claude/plugins/marketplaces/sf-skills/sf-flow/templates/[template].xml[project-root]/sf-flow/templates/[template].xmlExample: Read: ~/.claude/plugins/marketplaces/sf-skills/sf-flow/templates/record-triggered-flow-template.xml
Naming Convention (Recommended Prefixes):
| Flow Type | Prefix | Example |
|---|---|---|
| Record-Triggered (After) | Auto_ |
Auto_Lead_Assignment, Auto_Account_Update |
| Record-Triggered (Before) | Before_ |
Before_Lead_Validate, Before_Contact_Default |
| Screen Flow | Screen_ |
Screen_New_Customer, Screen_Case_Intake |
| Scheduled | Sched_ |
Sched_Daily_Cleanup, Sched_Weekly_Report |
| Platform Event | Event_ |
Event_Order_Completed |
| Autolaunched | Sub_ or Util_ |
Sub_Send_Email, Util_Validate_Address |
Format: [Prefix]_Object_Action using PascalCase (e.g., Auto_Lead_Priority_Assignment)
Screen Flow Button Config (CRITICAL):
| Screen | allowBack | allowFinish | Result |
|---|---|---|---|
| First | false | true | "Next" only |
| Middle | true | true | "Previous" + "Next" |
| Last | true | true | "Finish" |
Rule: allowFinish="true" required on all screens. Connector present → "Next", absent → "Finish".
Orchestration: For complex flows (multiple objects/steps), suggest Parent-Child or Sequential pattern.
docs/xml-gotchas.md (in sf-flow) and docs/orchestration-guide.md (in sf-flow)Create flow file:
mkdir -p force-app/main/default/flows
Write: force-app/main/default/flows/[FlowName].flow-meta.xml
Populate template: Replace placeholders, API Version: 65.0
CRITICAL Requirements:
<bulkSupport> (removed API 60.0+)Run Enhanced Validation (automatic via plugin hooks): The plugin automatically validates Flow XML files when written. Manual validation:
python3 ${CLAUDE_PLUGIN_ROOT}/hooks/scripts/validate_flow.py force-app/main/default/flows/[FlowName].flow-meta.xml
Validation (STRICT MODE):
New v2.0.0 Validations:
storeOutputAutomatically detection (data leak prevention)Run Simulation (REQUIRED for record-triggered/scheduled):
python3 ${CLAUDE_PLUGIN_ROOT}/hooks/scripts/simulate_flow.py force-app/main/default/flows/[FlowName].flow-meta.xml --test-records 200
If simulation fails: STOP and fix before proceeding.
Validation Report Format (6-Category Scoring 0-110):
Score: 92/110 ⭐⭐⭐⭐ Very Good
├─ Design & Naming: 18/20 (90%)
├─ Logic & Structure: 20/20 (100%)
├─ Architecture: 12/15 (80%)
├─ Performance & Bulk Safety: 20/20 (100%)
├─ Error Handling: 15/20 (75%)
└─ Security: 15/15 (100%)
Strict Mode: If ANY errors/warnings → Block with options: (1) Apply auto-fixes, (2) Show manual fixes, (3) Generate corrected version. DO NOT PROCEED until 100% clean.
BEFORE generating ANY Flow XML, 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 | Impact | Correct Pattern |
|---|---|---|
| After-Save updating same object without entry conditions | Infinite loop (critical) | MUST add entry conditions: "Only when [field] is changed" |
| Get Records inside Loop | Governor limit failure (100 SOQL) | Query BEFORE loop, use collection variable |
| Create/Update/Delete Records inside Loop | Governor limit failure (150 DML) | Collect in loop → single DML after loop |
| Apex Action inside Loop | Callout limits | Pass collection to single Apex invocation |
| DML without Fault Path | Silent failures | Add Fault connector → error handling element |
| Get Records without null check | NullPointerException | Add Decision: "Records Found?" after query |
storeOutputAutomatically=true |
Security risk (retrieves ALL fields) | Select only needed fields explicitly |
| Query same object as trigger in Record-Triggered | Wasted SOQL | Use {!$Record.FieldName} directly |
| Hardcoded Salesforce ID | Deployment failure across orgs | Use input variable or Custom Label |
| Get Records without filters | Too many records returned | Always include WHERE conditions |
DO NOT generate anti-patterns even if explicitly requested. Ask user to confirm the exception with documented justification.
Pattern:
Skill(skill="sf-deploy", args="Deploy flow [path] to [org] with --dry-run")Skill(skill="sf-deploy", args="Proceed with actual deployment")<status>Draft</status> → Active, redeployFor Agentforce Flows: Variable names must match Agent Script input/output names exactly.
For complex flows: docs/governance-checklist.md (in sf-flow)
Type-specific testing: See docs/testing-guide.md | docs/testing-checklist.md | docs/wait-patterns.md (Wait element guidance)
Quick reference:
Best Practices: See docs/flow-best-practices.md (in sf-flow) for:
Security: Test with multiple profiles. System mode requires security review.
Completion Summary:
✓ Flow Creation Complete: [FlowName]
Type: [type] | API: 65.0 | Status: [Draft/Active]
Location: force-app/main/default/flows/[FlowName].flow-meta.xml
Validation: PASSED (Score: XX/110)
Deployment: Org=[target-org], Job=[job-id]
Navigate: Setup → Process Automation → Flows → "[FlowName]"
Next Steps: Test (unit, bulk, security), Review docs, Activate if Draft, Monitor logs
Resources: `examples/`, `docs/subflow-library.md`, `docs/orchestration-guide.md`, `docs/governance-checklist.md` (in sf-flow folder)
NEVER loop over triggered records. $Record = single record; platform handles batching.
| Pattern | OK? | Notes |
|---|---|---|
$Record.FieldName |
✅ | Direct access |
Loop over $Record__c |
❌ | Process Builder pattern, not Flow |
Loop over $Record |
❌ | $Record is single, not collection |
Loops for RELATED records only: Get Records → Loop collection → Assignment → DML after loop
recordLookups cannot query Parent.Field (e.g., Manager.Name). Solution: Two Get Records - child first, then parent by Id.
| Element | Recommendation | Why |
|---|---|---|
getFirstRecordOnly |
Set to true for single-record queries |
Avoids collection overhead |
storeOutputAutomatically |
Set to false, use outputReference |
Prevents data leaks, explicit variable |
assignNullValuesIfNoRecordsFound |
Set to false |
Preserves previous variable value |
faultConnector |
Always include | Handle query failures gracefully |
filterLogic |
Use and for multiple filters |
Clear filter behavior |
All elements of the same type MUST be grouped together. Do NOT scatter elements across the file.
Complete alphabetical order:
apiVersion → assignments → constants → decisions → description → environments →
formulas → interviewLabel → label → loops → processMetadataValues → processType →
recordCreates → recordDeletes → recordLookups → recordUpdates → runInMode →
screens → start → status → subflows → textTemplates → variables → waits
Common Mistake: Adding an assignment near related logic (e.g., after a loop) when other assignments exist earlier.
var_ Regular variables (e.g., var_AccountName)col_ Collections (e.g., col_ContactIds)rec_ Record variables (e.g., rec_Account)inp_ Input variables (e.g., inp_RecordId)out_ Output variables (e.g., out_IsSuccess)Check_Account_Type)Action_[Verb]_[Object] (e.g., Action_Save_Contact)docs/flow-best-practices.md (in sf-flow) for comprehensive guidanceDML in Loop: Collect records in collection variable → Single DML after loop Missing Fault Path: Add fault connector from DML → error handling → log/display Self-Referencing Fault: Error "element cannot be connected to itself" → Route fault connector to DIFFERENT element Element Duplicated: Error "Element X is duplicated" → Group ALL elements of same type together Field Not Found: Verify field exists, deploy field first if missing Insufficient Permissions: Check profile permissions, consider System mode
| Error Pattern | Fix |
|---|---|
$Record__Prior in Create-only |
Only valid for Update/CreateAndUpdate triggers |
| "Parent.Field doesn't exist" | Use TWO Get Records (child then parent) |
$Record__c loop fails |
Use $Record directly (single context, not collection) |
XML Gotchas: See docs/xml-gotchas.md (in sf-flow)
| Scenario | Solution |
|---|---|
| >200 records | Warn limits, suggest scheduled flow |
| >5 branches | Use subflows |
| Cross-object | Check circular deps, test recursion |
| Production | Deploy Draft, activate explicitly |
| Unknown org | Use standard objects (Account, Contact, etc.) |
Debug: Flow not visible → deploy report + permissions | Tests fail → Debug Logs + bulk test | Sandbox→Prod fails → FLS + dependencies
| From Skill | To sf-flow | When |
|---|---|---|
| sf-ai-agentscript | → sf-flow | "Create Autolaunched Flow for agent action" |
| sf-apex | → sf-flow | "Create Flow wrapper for Apex logic" |
| sf-integration | → sf-flow | "Create HTTP Callout Flow" |
| From sf-flow | To Skill | When |
|---|---|---|
| sf-flow | → sf-metadata | "Describe Invoice__c" (verify fields before flow) |
| sf-flow | → sf-deploy | "Deploy flow with --dry-run" |
| sf-flow | → sf-data | "Create 200 test Accounts" (after deploy) |
Deployment: See Phase 4 above.
Embed custom Lightning Web Components in Flow Screens for rich, interactive UIs.
| Template | Purpose |
|---|---|
templates/screen-flow-with-lwc.xml |
Flow embedding LWC component |
templates/apex-action-template.xml |
Flow calling Apex @InvocableMethod |
<screens>
<fields>
<extensionName>c:recordSelector</extensionName>
<fieldType>ComponentInstance</fieldType>
<inputParameters>
<name>recordId</name>
<value><elementReference>var_RecordId</elementReference></value>
</inputParameters>
<outputParameters>
<assignToReference>var_SelectedId</assignToReference>
<name>selectedRecordId</name>
</outputParameters>
</fields>
</screens>
| Resource | Location |
|---|---|
| LWC Integration Guide | docs/lwc-integration-guide.md |
| LWC Component Setup | sf-lwc/docs/flow-integration-guide.md |
| Triangle Architecture | docs/triangle-pattern.md |
Call Apex @InvocableMethod classes from Flow for complex business logic.
<actionCalls>
<name>Process_Record</name>
<actionName>RecordProcessor</actionName>
<actionType>apex</actionType>
<inputParameters>
<name>recordId</name>
<value><elementReference>var_RecordId</elementReference></value>
</inputParameters>
<outputParameters>
<assignToReference>var_IsSuccess</assignToReference>
<name>isSuccess</name>
</outputParameters>
<faultConnector>
<targetReference>Handle_Error</targetReference>
</faultConnector>
</actionCalls>
| Resource | Location |
|---|---|
| Apex Action Template | templates/apex-action-template.xml |
| Apex @InvocableMethod Guide | sf-apex/docs/flow-integration.md |
| Triangle Architecture | docs/triangle-pattern.md |
When sf-ai-agentscript requests a Flow:
flow://FlowName targetsVariable Name Matching: When creating Flows for Agentforce agents:
inp_AccountId, out_AccountName)| Direction | Pattern |
|---|---|
| sf-flow → sf-metadata | "Describe Invoice__c" (verify fields before flow) |
| sf-flow → sf-deploy | Deploy with validation |
| sf-flow → sf-data | "Create 200 test Accounts" (test data after deploy) |
| sf-ai-agentscript → sf-flow | "Create Autolaunched Flow for agent action" - sf-flow is MANDATORY |
Dependencies (optional): sf-deploy, sf-metadata, sf-data | API: 65.0 | Mode: Strict (warnings block) | Python validators recommended
MIT License. See LICENSE file. Copyright (c) 2024-2025 Jag Valaiyapathy