---
name: sf-metadata
description: >
Generates and queries Salesforce metadata with 120-point scoring. Use when
creating custom objects, fields, profiles, permission sets, validation rules,
or querying org metadata structures via sf CLI.
license: MIT
metadata:
version: "1.1.0"
author: "Jag Valaiyapathy"
scoring: "120 points across 6 categories"
hooks:
PreToolUse:
- matcher: Bash
hooks:
- type: command
command: "python3 ${SHARED_HOOKS}/scripts/guardrails.py"
timeout: 5000
PostToolUse:
- matcher: Write
hooks:
- type: command
command: "python3 ${SKILL_HOOKS}/post-write-validate.py"
timeout: 10000
- type: command
command: "python3 ${SHARED_HOOKS}/suggest-related-skills.py sf-metadata"
timeout: 5000
- matcher: Edit
hooks:
- type: command
command: "python3 ${SHARED_HOOKS}/suggest-related-skills.py sf-metadata"
timeout: 5000
SubagentStop:
- type: command
command: "python3 ${SHARED_HOOKS}/scripts/chain-validator.py sf-metadata"
timeout: 5000
---
# sf-metadata: Salesforce Metadata Generation and Org Querying
Expert Salesforce administrator specializing in metadata architecture, security model design, and schema best practices. Generate production-ready metadata XML and query org structures using sf CLI v2.
## Core Responsibilities
1. **Metadata Generation**: Create Custom Objects, Fields, Profiles, Permission Sets, Validation Rules, Record Types, Page Layouts
2. **Org Querying**: Describe objects, list fields, query metadata using sf CLI v2
3. **Validation & Scoring**: Score metadata against 6 categories (0-120 points)
4. **Cross-Skill Integration**: Provide metadata discovery for sf-apex and sf-flow
5. **Deployment Integration**: Deploy metadata via sf-deploy skill
## ⚠️ CRITICAL: Orchestration Order
**sf-metadata → sf-flow → sf-deploy → sf-data** (you are here: sf-metadata)
⚠️ sf-data requires objects deployed to org. Always deploy BEFORE creating test data.
```
1. sf-metadata ◀── YOU ARE HERE (create objects/fields locally)
2. sf-flow → Create flow definitions (local)
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.
---
## ⚠️ CRITICAL: Field-Level Security
**Deployed fields are INVISIBLE until FLS is configured!** Always prompt for Permission Set generation after creating objects/fields. See **Phase 3.5** for auto-generation workflow.
---
## Workflow (5-Phase Pattern)
### Phase 1: Requirements Gathering
Use **AskUserQuestion** to gather:
- Operation type: **Generate** metadata OR **Query** org metadata
- If generating:
- Metadata type (Object, Field, Profile, Permission Set, Validation Rule, Record Type, Layout)
- Target object (for fields, validation rules, record types)
- Specific requirements (field type, data type, relationships, picklist values)
- If querying:
- Query type (describe object, list fields, list metadata)
- Target org alias
- Object name or metadata type to query
**Then**:
1. Check existing metadata: `Glob: **/*-meta.xml`, `Glob: **/objects/**/*.xml`
2. Check for sfdx-project.json to confirm Salesforce project structure
3. Create TodoWrite tasks
### Phase 2: Template Selection / Query Execution
#### For Generation
**Select template**:
| Metadata Type | Template |
|---------------|----------|
| Custom Object | `templates/objects/custom-object.xml` |
| Text Field | `templates/fields/text-field.xml` |
| Number Field | `templates/fields/number-field.xml` |
| Currency Field | `templates/fields/currency-field.xml` |
| Date Field | `templates/fields/date-field.xml` |
| Checkbox Field | `templates/fields/checkbox-field.xml` |
| Picklist Field | `templates/fields/picklist-field.xml` |
| Multi-Select Picklist | `templates/fields/multi-select-picklist.xml` |
| Lookup Field | `templates/fields/lookup-field.xml` |
| Master-Detail Field | `templates/fields/master-detail-field.xml` |
| Formula Field | `templates/fields/formula-field.xml` |
| Roll-Up Summary | `templates/fields/rollup-summary-field.xml` |
| Email Field | `templates/fields/email-field.xml` |
| Phone Field | `templates/fields/phone-field.xml` |
| URL Field | `templates/fields/url-field.xml` |
| Text Area (Long) | `templates/fields/textarea-field.xml` |
| Profile | `templates/profiles/profile.xml` |
| Permission Set | `templates/permission-sets/permission-set.xml` |
| Validation Rule | `templates/validation-rules/validation-rule.xml` |
| Record Type | `templates/record-types/record-type.xml` |
| Page Layout | `templates/layouts/page-layout.xml` |
**Template Path Resolution** (try in order):
1. **Marketplace folder**: `~/.claude/plugins/marketplaces/sf-skills/sf-metadata/templates/[path]`
2. **Project folder**: `[project-root]/sf-metadata/templates/[path]`
**Example**: `Read: ~/.claude/plugins/marketplaces/sf-skills/sf-metadata/templates/objects/custom-object.xml`
#### For Querying (sf CLI v2 Commands)
| Query Type | Command |
|------------|---------|
| Describe object | `sf sobject describe --sobject [ObjectName] --target-org [alias] --json` |
| List custom objects | `sf org list metadata --metadata-type CustomObject --target-org [alias] --json` |
| List all metadata types | `sf org list metadata-types --target-org [alias] --json` |
| List profiles | `sf org list metadata --metadata-type Profile --target-org [alias] --json` |
| List permission sets | `sf org list metadata --metadata-type PermissionSet --target-org [alias] --json` |
**Present query results** in structured format:
```
📊 Object: Account
════════════════════════════════════════
📁 Standard Fields: 45
📁 Custom Fields: 12
🔗 Relationships: 8
📝 Validation Rules: 3
📋 Record Types: 2
Custom Fields:
├── Industry_Segment__c (Picklist)
├── Annual_Revenue__c (Currency)
├── Primary_Contact__c (Lookup → Contact)
└── ...
```
### Phase 3: Generation / Validation
**For Generation**:
1. Create metadata file in appropriate directory:
- Objects: `force-app/main/default/objects/[ObjectName__c]/[ObjectName__c].object-meta.xml`
- Fields: `force-app/main/default/objects/[ObjectName]/fields/[FieldName__c].field-meta.xml`
- Profiles: `force-app/main/default/profiles/[ProfileName].profile-meta.xml`
- Permission Sets: `force-app/main/default/permissionsets/[PermSetName].permissionset-meta.xml`
- Validation Rules: `force-app/main/default/objects/[ObjectName]/validationRules/[RuleName].validationRule-meta.xml`
- Record Types: `force-app/main/default/objects/[ObjectName]/recordTypes/[RecordTypeName].recordType-meta.xml`
- Layouts: `force-app/main/default/layouts/[ObjectName]-[LayoutName].layout-meta.xml`
2. Populate template with user requirements
3. Apply naming conventions (see `docs/naming-conventions.md` in sf-metadata folder)
4. Run validation (automatic via hooks or manual)
**Validation Report Format** (6-Category Scoring 0-120):
```
Score: 105/120 ⭐⭐⭐⭐ Very Good
├─ Structure & Format: 20/20 (100%)
├─ Naming Conventions: 18/20 (90%)
├─ Data Integrity: 15/20 (75%)
├─ Security & FLS: 20/20 (100%)
├─ Documentation: 18/20 (90%)
└─ Best Practices: 14/20 (70%)
Issues:
⚠️ [Naming] Field API name should use PascalCase: 'account_status__c' → 'Account_Status__c'
⚠️ [Best Practice] Consider using Global Value Set for reusable picklist
```
### Phase 3.5: Permission Set Auto-Generation (NEW)
**After creating Custom Objects or Fields, ALWAYS prompt the user:**
```
AskUserQuestion:
question: "Would you like me to generate a Permission Set for [ObjectName__c] field access?"
header: "FLS Setup"
options:
- label: "Yes, generate Permission Set"
description: "Creates [ObjectName]_Access.permissionset-meta.xml with object CRUD and field access"
- label: "No, I'll handle FLS manually"
description: "Skip Permission Set generation - you'll configure FLS via Setup or Profile"
```
**If user selects "Yes":**
1. **Collect field information** from created metadata
2. **Filter out required fields** (they are auto-visible, cannot be in Permission Sets)
3. **Filter out formula fields** (can only be readable, not editable)
4. **Generate Permission Set** at: `force-app/main/default/permissionsets/[ObjectName]_Access.permissionset-meta.xml`
**Permission Set Generation Rules:**
| Field Type | Include in Permission Set? | Notes |
|------------|---------------------------|-------|
| Required fields | ❌ NO | Auto-visible, Salesforce rejects in Permission Set |
| Optional fields | ✅ YES | Include with `editable: true, readable: true` |
| Formula fields | ✅ YES | Include with `editable: false, readable: true` |
| Roll-Up Summary | ✅ YES | Include with `editable: false, readable: true` |
| Master-Detail | ❌ NO | Controlled by parent object permissions |
| Name field | ❌ NO | Always visible, cannot be in Permission Set |
**Example Auto-Generated Permission Set:**
```xml
Auto-generated: Grants access to Customer_Feedback__c and its fields
false
true
true
true
true
false
true
true
Customer_Feedback__c.Optional_Field__c
true
```
---
### Phase 4: Deployment
```
Skill(skill="sf-deploy", args="Deploy metadata at force-app/main/default/objects/[ObjectName] and permission set to [target-org]")
```
**Post-deployment** (optional - assign permission set):
```bash
sf org assign permset --name [ObjectName]_Access --target-org [alias]
```
### Phase 5: Verification
**For Generated Metadata**:
```
✓ Metadata Complete: [MetadataName]
Type: [CustomObject/CustomField/Profile/etc.] | API: 65.0
Location: force-app/main/default/[path]
Validation: PASSED (Score: XX/120)
Next Steps:
1. Verify in Setup → Object Manager → [Object]
2. Check Field-Level Security for new fields
3. Add to Page Layouts if needed
```
**For Queries**:
- Present results in structured format
- Highlight relevant information
- Offer follow-up actions (create field, modify permissions, etc.)
---
## Best Practices (Built-In Enforcement)
### Critical Requirements
**Structure & Format** (20 points):
- Valid XML syntax (-10 if invalid)
- Correct Salesforce namespace: `http://soap.sforce.com/2006/04/metadata` (-5 if missing)
- API version present and >= 65.0 (-5 if outdated)
- Correct file path and naming structure (-5 if wrong)
**Naming Conventions** (20 points):
- Custom objects/fields end with `__c` (-3 each violation)
- Use PascalCase for API names: `Account_Status__c` not `account_status__c` (-2 each)
- Meaningful labels (no abbreviations like `Acct`, `Sts`) (-2 each)
- Relationship names follow pattern: `[ParentObject]_[ChildObjects]` (-3)
**Data Integrity** (20 points):
- Required fields have sensible defaults or validation (-5)
- Number fields have appropriate precision/scale (-3)
- Picklist values properly defined with labels (-3)
- Relationship delete constraints specified (SetNull, Restrict, Cascade) (-3)
- Formula field syntax valid (-5)
- Roll-up summaries reference correct fields (-3)
**Security & FLS** (20 points):
- Field-Level Security considerations documented (-5 if sensitive field exposed)
- Sensitive field types flagged (SSN patterns, Credit Card patterns) (-10)
- Object sharing model appropriate for data sensitivity (-5)
- Permission Sets preferred over Profile modifications (advisory)
**Documentation** (20 points):
- Description present and meaningful on objects/fields (-5 if missing)
- Help text for user-facing fields (-3 each)
- Clear error messages for validation rules (-3)
- Inline comments in complex formulas (-3)
**Best Practices** (20 points):
- Use Permission Sets over Profiles when possible (-3 if Profile-first)
- Avoid hardcoded Record IDs in formulas (-5 if found)
- Use Global Value Sets for reusable picklists (advisory)
- Master-Detail vs Lookup selection appropriate for use case (-3)
- Record Types have associated Page Layouts (-3)
### Scoring
**Thresholds**: ⭐⭐⭐⭐⭐ 108+ | ⭐⭐⭐⭐ 96-107 | ⭐⭐⭐ 84-95 | Block: <72
---
## Field Template Tips
### Number Field: Omit Empty Defaults
**⚠️ Don't include `` if it's empty or zero - Salesforce ignores it:**
```xml
Score__c
Number
3
0
Score__c
Number
3
0
Priority__c
Number
1
0
3
```
### Standard vs Custom Object Paths
**⚠️ Standard objects use different path than custom objects:**
| Object Type | Path Example |
|-------------|--------------|
| Standard (Lead) | `objects/Lead/fields/Lead_Score__c.field-meta.xml` |
| Custom | `objects/MyObject__c/fields/MyField__c.field-meta.xml` |
**Common Mistake**: Using `Lead__c` (with suffix) for standard Lead object.
---
## Field Type Selection Guide
| Type | Salesforce | Notes |
|------|------------|-------|
| Text | Text / Text Area (Long/Rich) | ≤255 chars / multi-line / HTML |
| Numbers | Number / Currency | Decimals or money (org currency) |
| Boolean | Checkbox | True/False |
| Choice | Picklist / Multi-Select | Single/multiple predefined options |
| Date | Date / DateTime | With or without time |
| Contact | Email / Phone / URL | Validated formats |
| Relationship | Lookup / Master-Detail | Optional / required parent |
| Calculated | Formula / Roll-Up | Derived from fields / children |
---
## Relationship Decision Matrix
| Scenario | Use | Reason |
|----------|-----|--------|
| Parent optional | Lookup | Child can exist without parent |
| Parent required | Master-Detail | Cascade delete, roll-up summaries |
| Many-to-Many | Junction Object | Two Master-Detail relationships |
| Self-referential | Hierarchical Lookup | Same object (e.g., Account hierarchy) |
| Cross-object formula | Master-Detail or Formula | Access parent fields |
---
## Common Validation Rule Patterns
| Pattern | Formula | Use |
|---------|---------|-----|
| Conditional Required | `AND(ISPICKVAL(Status,'Closed'), ISBLANK(Close_Date__c))` | Field required when condition met |
| Email Regex | `NOT(REGEX(Email__c, "^[a-zA-Z0-9._-]+@..."))` | Format validation |
| Future Date | `Due_Date__c < TODAY()` | Date constraints |
| Cross-Object | `AND(Account.Type != 'Customer', Amount__c > 100000)` | Related field checks |
---
## Cross-Skill Integration
| From Skill | To sf-metadata | When |
|------------|----------------|------|
| sf-apex | → sf-metadata | "Describe Invoice__c" (discover fields before coding) |
| sf-flow | → sf-metadata | "Describe object fields, record types, validation rules" |
| sf-data | → sf-metadata | "Describe Custom_Object__c fields" (discover structure) |
| From sf-metadata | To Skill | When |
|------------------|----------|------|
| sf-metadata | → sf-deploy | "Deploy with --dry-run" (validate & deploy metadata) |
| sf-metadata | → sf-flow | After creating objects/fields that Flow will reference |
---
## Metadata Anti-Patterns
| Anti-Pattern | Fix |
|--------------|-----|
| Profile-based FLS | Use Permission Sets for granular access |
| Hardcoded IDs in formulas | Use Custom Settings or Custom Metadata |
| Validation rule without bypass | Add `$Permission.Bypass_Validation__c` check |
| Too many picklist values (>200) | Consider Custom Object instead |
| Auto-number without prefix | Add meaningful prefix: `INV-{0000}` |
| Roll-up on non-M-D | Use trigger-based calculation or DLRS |
| Field label = API name | Use user-friendly labels |
| No description on custom objects | Always document purpose |
---
## sf CLI Quick Reference
### Object & Field Queries
```bash
# Describe standard or custom object
sf sobject describe --sobject Account --target-org [alias] --json
# List all custom objects
sf org list metadata --metadata-type CustomObject --target-org [alias] --json
# List all custom fields on an object
sf org list metadata --metadata-type CustomField --folder Account --target-org [alias] --json
```
### Metadata Operations
```bash
# List all metadata types available
sf org list metadata-types --target-org [alias] --json
# Retrieve specific metadata
sf project retrieve start --metadata CustomObject:Account --target-org [alias]
# Generate package.xml from source
sf project generate manifest --source-dir force-app --name package.xml
```
### Interactive Generation
```bash
# Generate custom object interactively
sf schema generate sobject --label "My Object"
# Generate custom field interactively
sf schema generate field --label "My Field" --object Account
```
---
## Reference & Dependencies
**Docs**: `docs/` folder (in sf-metadata) - metadata-types-reference, field-types-guide, fls-best-practices, naming-conventions
- **Path**: `~/.claude/plugins/marketplaces/sf-skills/sf-metadata/docs/`
**Dependencies**: sf-deploy (optional) for deployment. Install: `/plugin install github:Jaganpro/sf-skills/sf-deploy`
**Notes**: API 65.0 required | Permission Sets over Profiles | Block if score < 72
---
## Validation
**Manual validation** (if hooks don't fire):
```bash
python3 ~/.claude/plugins/marketplaces/sf-skills/sf-metadata/hooks/scripts/validate_metadata.py
```
**Scoring**: 120 points / 6 categories. Minimum 84 (70%) for deployment.
**Hooks not firing?** Check: `CLAUDE_PLUGIN_ROOT` set, hooks.json valid, Python 3 in PATH, file matches pattern.
---
## 🔑 Key Insights
| Insight | Issue | Fix |
|---------|-------|-----|
| FLS is the Silent Killer | Deployed fields invisible without FLS | Always prompt for Permission Set generation |
| Required Fields ≠ Permission Sets | Salesforce rejects required fields in PS | Filter out required fields from fieldPermissions |
| Orchestration Order | sf-data fails if objects not deployed | sf-metadata → sf-flow → sf-deploy → sf-data |
| Before-Save Efficiency | Before-Save auto-saves, no DML needed | Use Before-Save for same-record updates |
| Test with 251 Records | Batch boundary at 200 records | Always bulk test with 251+ records |
## Common Errors
| Error | Fix |
|-------|-----|
| `Cannot deploy to required field` | Remove from fieldPermissions (auto-visible) |
| `Field does not exist` | Create Permission Set with field access |
| `SObject type 'X' not supported` | Deploy metadata first |
| `Element X is duplicated` | Reorder XML elements alphabetically |
---
## License
MIT License. See [LICENSE](LICENSE) file.
Copyright (c) 2024-2025 Jag Valaiyapathy