Salesforce Data Operations Expert (sf-data) Use this skill when the user needs Salesforce data work : record CRUD, bulk import/export, test data generation, cleanup scripts, or data factory patterns for validating Apex, Flow, or integration behavior. When This Skill Owns the Task Use when the work involves: - CLI commands - record creation, update, delete, upsert, export, or tree import/export - realistic test data generation - bulk data operations and cleanup - Apex anonymous scripts for data seeding / rollback Delegate elsewhere when the user is: - writing SOQL only → sf-soql - running or r…

, '', content, flags=re.MULTILINE)\n # Remove multi-line comments\n content = re.sub(r'/\\*[\\s\\S]*?\\*/', '', content)\n return content\n\n def _has_where_clause(self, content: str) -> bool:\n \"\"\"Check if query has a WHERE clause.\"\"\"\n return bool(re.search(r'\\bWHERE\\b', content, re.IGNORECASE))\n\n def _has_limit(self, content: str) -> bool:\n \"\"\"Check if query has a LIMIT clause.\"\"\"\n return bool(re.search(r'\\bLIMIT\\s+\\d+', content, re.IGNORECASE))\n\n def _has_hardcoded_ids(self, content: str) -> bool:\n \"\"\"Check for hardcoded Salesforce IDs.\"\"\"\n # Salesforce IDs are 15 or 18 character alphanumeric\n # Starting with specific prefixes: 001 (Account), 003 (Contact), etc.\n id_pattern = r\"'[a-zA-Z0-9]{15}'\" # 15-char ID\n id_pattern_18 = r\"'[a-zA-Z0-9]{18}'\" # 18-char ID\n\n return bool(re.search(id_pattern, content) or re.search(id_pattern_18, content))\n\n def _uses_indexed_fields(self, content: str) -> bool:\n \"\"\"Check if WHERE clause uses indexed fields.\"\"\"\n # Extract WHERE clause\n where_match = re.search(r'\\bWHERE\\b(.*?)(?:\\bORDER\\b|\\bGROUP\\b|\\bLIMIT\\b|$)',\n content, re.IGNORECASE | re.DOTALL)\n if not where_match:\n return False\n\n where_clause = where_match.group(1)\n\n # Check for indexed fields\n for field in self.INDEXED_FIELDS:\n if re.search(rf'\\b{field}\\b', where_clause, re.IGNORECASE):\n return True\n\n return False\n\n def _has_subquery(self, content: str) -> bool:\n \"\"\"Check if query has subqueries.\"\"\"\n # Look for nested SELECT in parentheses\n return bool(re.search(r'\\(\\s*SELECT\\b', content, re.IGNORECASE))\n\n def _has_relationship(self, content: str) -> bool:\n \"\"\"Check if query uses relationship queries.\"\"\"\n # Look for dot notation (child-to-parent) or __r suffix\n return bool(re.search(r'\\w+\\.\\w+|__r\\.', content))\n\n def _validate_syntax(self, content: str) -> List[Dict[str, Any]]:\n \"\"\"Validate SOQL syntax and return issues.\"\"\"\n issues = []\n\n # Check for SELECT without FROM\n if re.search(r'\\bSELECT\\b', content, re.IGNORECASE):\n if not re.search(r'\\bFROM\\b', content, re.IGNORECASE):\n issues.append({\n 'severity': 'error',\n 'message': 'SELECT statement missing FROM clause'\n })\n\n # Check for invalid operators\n if re.search(r'==', content):\n issues.append({\n 'severity': 'error',\n 'message': 'Invalid operator \"==\" - use \"=\" in SOQL'\n })\n\n if re.search(r'!=\\s*null', content, re.IGNORECASE):\n pass # Valid\n elif re.search(r'\u003c>', content):\n issues.append({\n 'severity': 'warning',\n 'message': 'Consider using \"!=\" instead of \"\u003c>\" for consistency'\n })\n\n # Check for SELECT *\n if re.search(r'\\bSELECT\\s+\\*', content, re.IGNORECASE):\n issues.append({\n 'severity': 'error',\n 'message': 'SELECT * is not valid in SOQL - specify field names'\n })\n\n # Check for proper string quoting\n if re.search(r\"=\\s*\\\"[^\\\"]*\\\"\", content):\n issues.append({\n 'severity': 'warning',\n 'message': 'Use single quotes for string literals in SOQL'\n })\n\n # Check for unbalanced parentheses\n open_parens = content.count('(')\n close_parens = content.count(')')\n if open_parens != close_parens:\n issues.append({\n 'severity': 'error',\n 'message': f'Unbalanced parentheses: {open_parens} open, {close_parens} close'\n })\n\n # Check for TYPEOF without END\n if re.search(r'\\bTYPEOF\\b', content, re.IGNORECASE):\n if not re.search(r'\\bEND\\b', content, re.IGNORECASE):\n issues.append({\n 'severity': 'error',\n 'message': 'TYPEOF expression missing END keyword'\n })\n\n # Check for reserved words as field names (common issues)\n reserved = ['SELECT', 'FROM', 'WHERE', 'ORDER', 'GROUP', 'LIMIT']\n for word in reserved:\n # Look for patterns like \"SELECT SELECT\" or \"field, SELECT\"\n if re.search(rf'\\b{word}\\s*,|\\,\\s*{word}\\b', content, re.IGNORECASE):\n issues.append({\n 'severity': 'warning',\n 'message': f'Possible misuse of reserved word \"{word}\"'\n })\n\n return issues\n\n def get_query_complexity(self, content: str) -> Dict[str, int]:\n \"\"\"Analyze query complexity metrics.\"\"\"\n clean = self._remove_comments(content)\n\n return {\n 'select_fields': len(re.findall(r'\\bSELECT\\b.*?\\bFROM\\b', clean, re.IGNORECASE | re.DOTALL)),\n 'where_conditions': len(re.findall(r'\\b(AND|OR)\\b', clean, re.IGNORECASE)) + 1 if self._has_where_clause(clean) else 0,\n 'subqueries': len(re.findall(r'\\(\\s*SELECT\\b', clean, re.IGNORECASE)),\n 'joins': len(re.findall(r'__r\\.|\\.Name|\\.Id', clean)),\n 'aggregates': len(re.findall(r'\\b(COUNT|SUM|AVG|MIN|MAX)\\s*\\(', clean, re.IGNORECASE))\n }\n\n def suggest_optimizations(self, content: str) -> List[str]:\n \"\"\"Suggest query optimizations.\"\"\"\n suggestions = []\n clean = self._remove_comments(content)\n\n # Check for missing indexed field in WHERE\n if self._has_where_clause(clean) and not self._uses_indexed_fields(clean):\n suggestions.append('Add an indexed field (Id, Name, CreatedDate) to WHERE for better performance')\n\n # Check for ORDER BY without LIMIT\n if re.search(r'\\bORDER\\s+BY\\b', clean, re.IGNORECASE):\n if not self._has_limit(clean):\n suggestions.append('Consider adding LIMIT when using ORDER BY')\n\n # Check for SELECT with many fields\n select_match = re.search(r'\\bSELECT\\b(.*?)\\bFROM\\b', clean, re.IGNORECASE | re.DOTALL)\n if select_match:\n fields = select_match.group(1)\n field_count = len([f.strip() for f in fields.split(',') if f.strip()])\n if field_count > 20:\n suggestions.append(f'Query selects {field_count} fields - consider selecting only needed fields')\n\n # Check for deeply nested subqueries\n subquery_depth = len(re.findall(r'\\(\\s*SELECT\\b', clean, re.IGNORECASE))\n if subquery_depth > 2:\n suggestions.append('Consider simplifying query - deeply nested subqueries may impact performance')\n\n return suggestions\n\n\n# Standalone execution for testing\nif __name__ == '__main__':\n import sys\n\n if len(sys.argv) \u003c 2:\n print('Usage: python soql_validator.py \u003csoql_file>')\n sys.exit(1)\n\n with open(sys.argv[1], 'r') as f:\n content = f.read()\n\n validator = SOQLValidator(content)\n result = validator.validate()\n\n print('SOQL Validation Results:')\n print('=' * 40)\n for key, value in result.items():\n if key not in ['issues', 'recommendations']:\n print(f'{key}: {value}')\n\n if result['issues']:\n print('\\nIssues:')\n for issue in result['issues']:\n print(f\" [{issue['severity']}] {issue['message']}\")\n\n if result['recommendations']:\n print('\\nRecommendations:')\n for rec in result['recommendations']:\n print(f' - {rec}')\n","content_type":"text/x-python; charset=utf-8","language":"python","size":11201,"content_sha256":"035e4a3620b8e127532b610ab09efb1e270b22539150aef0e5aa053d2c6db887"},{"filename":"hooks/scripts/validate_data_operation.py","content":"#!/usr/bin/env python3\n\"\"\"\nsf-data Validation Module\n=========================\n\nValidates data operation files against the 130-point scoring system:\n- Query Efficiency (25 points)\n- Bulk Safety (25 points)\n- Data Integrity (20 points)\n- Security & FLS (20 points)\n- Test Patterns (15 points)\n- Cleanup & Isolation (15 points)\n- Documentation (10 points)\n\"\"\"\n\nimport re\nfrom pathlib import Path\nfrom typing import Dict, List, Any, Optional\n\nclass DataOperationValidator:\n \"\"\"Validates data operation files.\"\"\"\n\n # Scoring categories\n CATEGORIES = {\n 'query_efficiency': {\n 'name': 'Query Efficiency',\n 'max': 25,\n 'score': 25,\n 'issues': []\n },\n 'bulk_safety': {\n 'name': 'Bulk Safety',\n 'max': 25,\n 'score': 25,\n 'issues': []\n },\n 'data_integrity': {\n 'name': 'Data Integrity',\n 'max': 20,\n 'score': 20,\n 'issues': []\n },\n 'security_fls': {\n 'name': 'Security & FLS',\n 'max': 20,\n 'score': 20,\n 'issues': []\n },\n 'test_patterns': {\n 'name': 'Test Patterns',\n 'max': 15,\n 'score': 15,\n 'issues': []\n },\n 'cleanup_isolation': {\n 'name': 'Cleanup & Isolation',\n 'max': 15,\n 'score': 15,\n 'issues': []\n },\n 'documentation': {\n 'name': 'Documentation',\n 'max': 10,\n 'score': 10,\n 'issues': []\n }\n }\n\n def __init__(self, file_path: str):\n self.file_path = Path(file_path)\n self.content = ''\n self.file_type = ''\n self.issues: List[Dict[str, Any]] = []\n self.recommendations: List[str] = []\n self.categories = self._init_categories()\n\n def _init_categories(self) -> Dict[str, Dict[str, Any]]:\n \"\"\"Initialize fresh category scores.\"\"\"\n import copy\n return copy.deepcopy(self.CATEGORIES)\n\n def validate(self) -> Optional[Dict[str, Any]]:\n \"\"\"Run validation and return results.\"\"\"\n if not self.file_path.exists():\n return None\n\n try:\n with open(self.file_path, 'r', encoding='utf-8') as f:\n self.content = f.read()\n except Exception:\n return None\n\n # Determine file type\n self.file_type = self.file_path.suffix.lower()\n\n # Run type-specific validation\n if self.file_type == '.apex':\n self._validate_apex()\n elif self.file_type == '.soql':\n self._validate_soql()\n elif self.file_type == '.csv':\n self._validate_csv()\n elif self.file_type == '.json':\n self._validate_json()\n else:\n return None\n\n # Calculate total score\n total_score = sum(cat['score'] for cat in self.categories.values())\n max_score = sum(cat['max'] for cat in self.categories.values())\n\n return {\n 'score': total_score,\n 'max_score': max_score,\n 'categories': {cat['name']: {'score': cat['score'], 'max': cat['max']}\n for cat in self.categories.values()},\n 'issues': self.issues,\n 'recommendations': self.recommendations\n }\n\n def _validate_apex(self):\n \"\"\"Validate Apex data operation file.\"\"\"\n content = self.content\n\n # Query Efficiency (25 points)\n self._check_query_efficiency(content)\n\n # Bulk Safety (25 points)\n self._check_bulk_safety(content)\n\n # Data Integrity (20 points)\n self._check_data_integrity(content)\n\n # Security & FLS (20 points)\n self._check_security(content)\n\n # Test Patterns (15 points)\n self._check_test_patterns(content)\n\n # Cleanup & Isolation (15 points)\n self._check_cleanup(content)\n\n # Documentation (10 points)\n self._check_documentation(content)\n\n def _validate_soql(self):\n \"\"\"Validate SOQL query file.\"\"\"\n content = self.content\n\n # Import SOQL validator\n try:\n from soql_validator import SOQLValidator\n soql_validator = SOQLValidator(content)\n soql_result = soql_validator.validate()\n\n # Apply SOQL-specific scoring\n if soql_result.get('has_where_clause', False):\n pass # Good\n else:\n self._deduct('query_efficiency', 5, 'Missing WHERE clause')\n\n if soql_result.get('has_limit', False):\n pass # Good\n else:\n self._deduct('query_efficiency', 3, 'Missing LIMIT clause')\n\n if soql_result.get('has_hardcoded_ids', False):\n self._deduct('query_efficiency', 5, 'Hardcoded record IDs found')\n\n except ImportError:\n # Basic SOQL validation\n if 'WHERE' not in content.upper():\n self._deduct('query_efficiency', 5, 'Missing WHERE clause')\n if 'LIMIT' not in content.upper():\n self._deduct('query_efficiency', 3, 'Missing LIMIT clause')\n\n # Documentation check for SOQL\n if not content.strip().startswith('--'):\n self._deduct('documentation', 5, 'Missing SOQL documentation/comments')\n\n def _validate_csv(self):\n \"\"\"Validate CSV import file.\"\"\"\n lines = self.content.strip().split('\\n')\n\n if len(lines) \u003c 2:\n self._deduct('data_integrity', 10, 'CSV file has no data rows')\n return\n\n # Check for header row\n header = lines[0]\n if not header or ',' not in header:\n self._deduct('data_integrity', 5, 'CSV missing proper header row')\n\n # Check for consistent column count\n expected_cols = len(header.split(','))\n for i, line in enumerate(lines[1:], 2):\n if line.strip() and len(line.split(',')) != expected_cols:\n self._deduct('data_integrity', 2, f'Inconsistent column count on line {i}')\n break\n\n # Check for potential PII patterns\n self._check_csv_security(self.content)\n\n def _validate_json(self):\n \"\"\"Validate JSON tree import file.\"\"\"\n import json as json_lib\n\n try:\n data = json_lib.loads(self.content)\n except json_lib.JSONDecodeError as e:\n self._deduct('data_integrity', 20, f'Invalid JSON: {str(e)}')\n return\n\n # Check for proper sObject tree format\n if 'records' not in data:\n self._deduct('data_integrity', 10, 'Missing \"records\" array')\n return\n\n records = data.get('records', [])\n\n for i, record in enumerate(records):\n if 'attributes' not in record:\n self._deduct('data_integrity', 5, f'Record {i+1} missing \"attributes\"')\n else:\n attrs = record['attributes']\n if 'type' not in attrs:\n self._deduct('data_integrity', 3, f'Record {i+1} missing object type')\n if 'referenceId' not in attrs:\n self._deduct('data_integrity', 2, f'Record {i+1} missing referenceId')\n\n def _check_query_efficiency(self, content: str):\n \"\"\"Check for query efficiency issues.\"\"\"\n # Check for queries in loops\n if re.search(r'for\\s*\\([^)]*\\)\\s*\\{[^}]*\\[SELECT', content, re.IGNORECASE | re.DOTALL):\n self._deduct('query_efficiency', 10, 'SOQL query inside for loop (N+1 pattern)')\n\n # Check for hardcoded IDs\n if re.search(r\"'[a-zA-Z0-9]{15,18}'\", content):\n self._deduct('query_efficiency', 5, 'Hardcoded Salesforce ID found')\n\n # Check for SELECT * equivalent (all fields)\n if re.search(r'SELECT\\s+\\*', content, re.IGNORECASE):\n self._deduct('query_efficiency', 5, 'SELECT * is not valid in SOQL')\n\n def _check_bulk_safety(self, content: str):\n \"\"\"Check for bulk safety issues.\"\"\"\n # Check for DML in loops\n dml_in_loop = re.search(\n r'for\\s*\\([^)]*\\)\\s*\\{[^}]*(insert|update|delete|upsert)\\s+',\n content, re.IGNORECASE | re.DOTALL\n )\n if dml_in_loop:\n self._deduct('bulk_safety', 10, 'DML operation inside for loop')\n\n # Check for single-record operations when bulk would be better\n single_record_pattern = re.search(\n r'(insert|update|delete)\\s+\\w+\\s*;(?!\\s*//\\s*single)',\n content, re.IGNORECASE\n )\n # This is a soft check - single records are sometimes intentional\n\n # Check for list-based DML (good pattern)\n if re.search(r'(insert|update|delete)\\s+\\w+List', content, re.IGNORECASE):\n pass # Good - using list-based DML\n elif re.search(r'(insert|update|delete)\\s+new\\s+List\u003c', content, re.IGNORECASE):\n pass # Good - using list\n\n def _check_data_integrity(self, content: str):\n \"\"\"Check for data integrity issues.\"\"\"\n # Check for required field handling\n if 'required' in content.lower() or 'mandatory' in content.lower():\n pass # Awareness of required fields\n\n # Check for null checks\n if re.search(r'!=\\s*null|!= null', content):\n pass # Good - null checking\n\n def _check_security(self, content: str):\n \"\"\"Check for security issues.\"\"\"\n # Check for PII patterns\n pii_patterns = [\n (r'\\b\\d{3}-\\d{2}-\\d{4}\\b', 'SSN pattern detected'),\n (r'\\b\\d{4}[-\\s]?\\d{4}[-\\s]?\\d{4}[-\\s]?\\d{4}\\b', 'Credit card pattern detected'),\n (r'\\b[A-Za-z0-9._%+-]+@(gmail|yahoo|hotmail|outlook)\\.(com|net|org)\\b',\n 'Personal email domain in test data')\n ]\n\n for pattern, message in pii_patterns:\n if re.search(pattern, content):\n self._deduct('security_fls', 10, message)\n\n # Check for WITH USER_MODE usage (good practice)\n if 'WITH USER_MODE' in content.upper():\n pass # Good - respecting FLS\n\n def _check_test_patterns(self, content: str):\n \"\"\"Check for good test data patterns.\"\"\"\n # Check for bulk record creation (200+)\n bulk_patterns = [\n r'(\\d{3,})\\s*[;,)]', # Numbers 100+\n r'count\\s*[=:]\\s*(\\d{3,})',\n r'recordCount\\s*[=:]\\s*(\\d{3,})'\n ]\n\n has_bulk = False\n for pattern in bulk_patterns:\n match = re.search(pattern, content)\n if match:\n try:\n num = int(match.group(1))\n if num >= 200:\n has_bulk = True\n except ValueError:\n pass\n\n # Check for edge case handling\n edge_cases = ['null', 'empty', 'boundary', 'special character', 'unicode']\n has_edge_cases = any(ec in content.lower() for ec in edge_cases)\n\n if not has_bulk and 'bulk' not in self.file_path.stem.lower():\n self._deduct('test_patterns', 5, 'Consider testing with 200+ records for bulkification')\n\n def _check_cleanup(self, content: str):\n \"\"\"Check for cleanup and isolation patterns.\"\"\"\n # Check for cleanup provisions\n cleanup_patterns = ['cleanup', 'delete', 'rollback', 'Savepoint', 'Database.rollback']\n has_cleanup = any(pattern.lower() in content.lower() for pattern in cleanup_patterns)\n\n if not has_cleanup:\n self._deduct('cleanup_isolation', 5, 'No cleanup mechanism found')\n self.recommendations.append('Add cleanup script or savepoint for test isolation')\n\n # Check for ID tracking\n if re.search(r'Set\u003cId>|List\u003cId>|createdIds|recordIds', content):\n pass # Good - tracking created IDs\n\n def _check_documentation(self, content: str):\n \"\"\"Check for documentation.\"\"\"\n # Check for file header comment\n if not (content.strip().startswith('/*') or\n content.strip().startswith('//') or\n content.strip().startswith('/**')):\n self._deduct('documentation', 3, 'Missing file header documentation')\n\n # Check for method documentation\n if re.search(r'/\\*\\*[\\s\\S]*?\\*/', content):\n pass # Has JSDoc-style comments\n elif re.search(r'//.*description|//.*purpose|//.*usage', content, re.IGNORECASE):\n pass # Has inline documentation\n else:\n self._deduct('documentation', 2, 'Consider adding method/section documentation')\n\n def _check_csv_security(self, content: str):\n \"\"\"Check CSV content for security issues.\"\"\"\n # Check for PII patterns in CSV\n lines = content.split('\\n')\n for i, line in enumerate(lines, 1):\n # SSN pattern\n if re.search(r'\\b\\d{3}-\\d{2}-\\d{4}\\b', line):\n self._deduct('security_fls', 10, f'SSN pattern on line {i}')\n break\n # Credit card pattern\n if re.search(r'\\b\\d{4}[-\\s]?\\d{4}[-\\s]?\\d{4}[-\\s]?\\d{4}\\b', line):\n self._deduct('security_fls', 10, f'Credit card pattern on line {i}')\n break\n\n def _deduct(self, category: str, points: int, message: str):\n \"\"\"Deduct points from a category and record the issue.\"\"\"\n if category in self.categories:\n current = self.categories[category]['score']\n self.categories[category]['score'] = max(0, current - points)\n self.categories[category]['issues'].append(message)\n\n self.issues.append({\n 'category': self.categories.get(category, {}).get('name', category),\n 'severity': 'error' if points >= 10 else 'warning',\n 'message': message,\n 'points': points\n })\n","content_type":"text/x-python; charset=utf-8","language":"python","size":13787,"content_sha256":"1a59af6c8d2753058432b00aa4c9f8349a067d243f472c3292b23235af5a63ac"},{"filename":"README.md","content":"# sf-data\n\nSalesforce data operations skill for Claude Code. Create, validate, bulk-load, clean up, and troubleshoot org data with a describe-first workflow.\n\n## Features\n\n- **CRUD Operations**: Create, read, update, delete records via sf CLI\n- **Describe-First Validation**: Inspect required fields, picklists, and createable fields before data creation\n- **CLI-First Test Data**: Prefer `sf data` commands for straightforward seed data\n- **Bulk Operations**: Import/export via Bulk API 2.0\n- **Record Tracking & Cleanup**: Savepoint/rollback, cleanup scripts, delete-by-pattern workflows\n- **130-Point Validation**: Automated scoring across 7 categories\n\n## Installation\n\n```bash\nnpx skills add Jaganpro/sf-skills --skill sf-data\n```\n\nOr install the complete sf-skills suite:\n```bash\nnpx skills add Jaganpro/sf-skills\n```\n\n## Usage\n\nInvoke the skill:\n```\nSkill(skill=\"sf-data\")\nRequest: \"Create 251 test Account records with varying Industries for trigger testing in org dev\"\n```\n\n### Common Operations\n\n| Operation | Example Request |\n|-----------|-----------------|\n| Query | \"Query all Accounts with related Contacts in org dev\" |\n| Create | \"Create 10 test Opportunities at various stages\" |\n| Bulk Insert | \"Insert 500 accounts from accounts.csv\" |\n| Update | \"Update Account 001xxx with new Industry\" |\n| Delete | \"Delete all test records with Name LIKE 'Test%'\" |\n| Cleanup | \"Generate cleanup script for all records created today\" |\n\n## Recommended workflow\n\n1. Describe the target object when schema constraints are uncertain\n2. Validate required fields, picklist values, and createable fields\n3. Choose CLI, tree import, bulk import, or anonymous Apex intentionally\n4. Verify results and provide cleanup instructions\n\n## Validation Scoring (130 Points)\n\n| Category | Points | Focus |\n|----------|--------|-------|\n| Query Efficiency | 25 | SOQL selectivity, indexed fields, no N+1 |\n| Bulk Safety | 25 | Governor limits, batch sizing |\n| Data Integrity | 20 | Required fields, relationships |\n| Security & FLS | 20 | Field-level security, no PII |\n| Test Patterns | 15 | 200+ records, edge cases |\n| Cleanup & Isolation | 15 | Rollback, cleanup scripts |\n| Documentation | 10 | Purpose, expected outcomes |\n\n## Cross-Skill Integration\n\n### With sf-metadata\n```\nSkill(skill=\"sf-metadata\")\nRequest: \"Describe Invoice__c in org dev - show all fields\"\n```\nThen use sf-data with accurate field names and permitted values.\n\n### From sf-apex / sf-flow\n```\nSkill(skill=\"sf-data\")\nRequest: \"Create 251 test Accounts to trigger AccountTrigger bulk testing\"\n```\n\n## Directory Structure\n\n```\nsf-data/\n├── SKILL.md # Main skill prompt\n├── assets/\n│ ├── factories/ # Apex test data factories\n│ ├── soql/ # SOQL query templates\n│ ├── bulk/ # Bulk operation templates\n│ ├── csv/ # CSV import templates\n│ ├── json/ # JSON tree templates\n│ └── cleanup/ # Cleanup scripts\n├── hooks/ # Validation hooks\n└── references/ # Documentation and examples\n```\n\n## Key documentation\n\n- [SKILL.md](SKILL.md) - Full workflow and orchestration guidance\n- [references/test-data-best-practices.md](references/test-data-best-practices.md) - Describe-first validation and retry strategy\n- [references/sf-cli-data-commands.md](references/sf-cli-data-commands.md) - sf CLI command patterns\n- [references/cleanup-rollback-guide.md](references/cleanup-rollback-guide.md) - Cleanup strategies\n\n## sf CLI Commands Wrapped\n\n| Operation | Command |\n|-----------|---------|\n| Query | `sf data query --query \"...\" --target-org [alias]` |\n| Create | `sf data create record --sobject [Object] --values \"...\"` |\n| Update | `sf data update record --sobject [Object] --record-id [Id] --values \"...\"` |\n| Delete | `sf data delete record --sobject [Object] --record-id [Id]` |\n| Bulk Import | `sf data import bulk --file [csv] --sobject [Object]` |\n| Bulk Delete | `sf data delete bulk --file [csv] --sobject [Object]` |\n| Tree Export | `sf data export tree --query \"...\" --output-dir [dir]` |\n| Tree Import | `sf data import tree --files [json]` |\n| Apex Run | `sf apex run --file [script.apex]` |\n\n## Requirements\n\n- sf CLI v2\n- Target Salesforce org (sandbox or production)\n- Claude Code with skill plugins enabled\n\n## License\n\nMIT License. See LICENSE file.\nCopyright (c) 2024-2026 Jag Valaiyapathy\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":4494,"content_sha256":"9c8bcb84b12caa37c1a6a2ab80a15bd7a0296cdc06adce591a0dd03068f3d25e"},{"filename":"references/anonymous-apex-guide.md","content":"\u003c!-- Parent: sf-data/SKILL.md -->\n# Anonymous Apex Guide\n\nUsing anonymous Apex for complex data operations.\n\n## When to Use Anonymous Apex\n\n- Complex data setup requiring Apex logic\n- Testing triggers with specific data patterns\n- One-time data migrations\n- Debugging and troubleshooting\n\n## sf CLI Execution\n\n### From File\n```bash\nsf apex run --file setup-data.apex --target-org myorg\n```\n\n### Interactive\n```bash\nsf apex run --target-org myorg\n# Then type Apex code and press Ctrl+D\n```\n\n## Common Patterns\n\n### Bulk Data Creation\n```apex\nList\u003cAccount> accounts = new List\u003cAccount>();\nfor (Integer i = 0; i \u003c 500; i++) {\n accounts.add(new Account(\n Name = 'Test Account ' + i,\n Industry = 'Technology'\n ));\n}\ninsert accounts;\nSystem.debug('Created ' + accounts.size() + ' accounts');\n```\n\n### Data Transformation\n```apex\nList\u003cAccount> accounts = [\n SELECT Id, Name, Industry\n FROM Account\n WHERE Name LIKE 'Old%'\n];\n\nfor (Account acc : accounts) {\n acc.Name = acc.Name.replace('Old', 'New');\n}\n\nupdate accounts;\n```\n\n### Testing Trigger Logic\n```apex\n// Setup test data\nAccount acc = new Account(Name = 'Trigger Test');\ninsert acc;\n\n// Force trigger to fire\nacc.Industry = 'Technology';\nupdate acc;\n\n// Verify results\nacc = [SELECT Id, Field__c FROM Account WHERE Id = :acc.Id];\nSystem.debug('Result: ' + acc.Field__c);\n```\n\n## Error Handling\n\n```apex\ntry {\n insert accounts;\n} catch (DmlException e) {\n System.debug('Error: ' + e.getMessage());\n for (Integer i = 0; i \u003c e.getNumDml(); i++) {\n System.debug('Row ' + e.getDmlIndex(i) + ': ' + e.getDmlMessage(i));\n }\n}\n```\n\n## Limits in Anonymous Apex\n\n| Limit | Value |\n|-------|-------|\n| SOQL Queries | 100 |\n| DML Rows | 10,000 |\n| CPU Time | 10,000 ms |\n| Heap Size | 6 MB |\n\n## Best Practices\n\n1. **Test in sandbox first** - Validate before production\n2. **Add debug statements** - Track progress\n3. **Handle errors gracefully** - Use try/catch\n4. **Keep scripts idempotent** - Safe to re-run\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":2001,"content_sha256":"cc99f47cac22871b9d1c50d77655e913e117f4656edc317b5300551f3feb5d4b"},{"filename":"references/bulk-operations-guide.md","content":"\u003c!-- Parent: sf-data/SKILL.md -->\n# Bulk Operations Guide\n\nWhen and how to use Salesforce Bulk API operations.\n\n## Decision Matrix\n\n| Record Count | Recommended API | Command |\n|--------------|-----------------|---------|\n| 1-10 | Single Record | `sf data create record` |\n| 11-2000 | Standard API | `sf data query` + Apex |\n| 2000-10M | Bulk API 2.0 | `sf data import bulk` |\n| 10M+ | Data Loader | External tool |\n\n## Bulk API 2.0 Commands\n\n### Import (Insert)\n```bash\nsf data import bulk \\\n --file accounts.csv \\\n --sobject Account \\\n --target-org myorg \\\n --wait 30\n```\n\n### Update\n```bash\nsf data update bulk \\\n --file updates.csv \\\n --sobject Account \\\n --target-org myorg \\\n --wait 30\n```\n\n### Upsert (Insert or Update)\n```bash\nsf data upsert bulk \\\n --file upsert.csv \\\n --sobject Account \\\n --external-id External_Id__c \\\n --target-org myorg \\\n --wait 30\n```\n\n### Delete\n```bash\nsf data delete bulk \\\n --file delete.csv \\\n --sobject Account \\\n --target-org myorg \\\n --wait 30\n```\n\n### Export\n```bash\nsf data export bulk \\\n --query \"SELECT Id, Name FROM Account\" \\\n --output-file accounts.csv \\\n --target-org myorg \\\n --wait 30\n```\n\n## CSV Format Requirements\n\n- First row: Field API names\n- UTF-8 encoding\n- Comma delimiter (default)\n- Max 100MB per file\n\n## Bulk API Limits\n\n| Limit | Value |\n|-------|-------|\n| Batches per 24 hours | 10,000 |\n| Records per 24 hours | 10,000,000 |\n| Max file size | 100 MB |\n| Max concurrent jobs | 100 |\n\n## Error Handling\n\n```bash\n# Check job status\nsf data resume --job-id [job-id] --target-org myorg\n\n# Get results\nsf data bulk results --job-id [job-id] --target-org myorg\n```\n\n## Best Practices\n\n1. **Chunk large files** - Split files >100MB\n2. **Use --wait** - Monitor completion\n3. **Handle partial failures** - Check result files\n4. **Test in sandbox** - Validate before production\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":1856,"content_sha256":"05734faa407d582316f9b79456c74dd902f6296f4bd58a807d14836923a09400"},{"filename":"references/bulk-testing-example.md","content":"\u003c!-- Parent: sf-data/SKILL.md -->\n# Bulk Testing Example\n\nTesting Apex triggers and flows with bulk data operations.\n\n## Scenario\n\nTest an Account trigger that:\n- Fires on insert with 200+ records\n- Updates a custom field based on Industry\n- Creates a related Task for high-value accounts\n\n## Why 201 Records?\n\nSalesforce processes triggers in batches of 200. Testing with 201+ records ensures:\n- Trigger handles batch boundaries\n- No governor limit violations in loops\n- SOQL/DML operations are bulkified\n\n## Method 1: Anonymous Apex Factory\n\n### Create Test Data\n```apex\n// Create 251 Accounts to test trigger bulkification\nList\u003cAccount> accounts = new List\u003cAccount>();\n\nList\u003cString> industries = new List\u003cString>{\n 'Technology', 'Healthcare', 'Finance', 'Manufacturing', 'Retail'\n};\n\nfor (Integer i = 0; i \u003c 251; i++) {\n accounts.add(new Account(\n Name = 'BulkTest Account ' + i,\n Industry = industries[Math.mod(i, industries.size())],\n AnnualRevenue = Math.round(Math.random() * 10000000)\n ));\n}\n\n// Single DML - triggers fire in batches of 200\ninsert accounts;\n\nSystem.debug('Created ' + accounts.size() + ' accounts');\nSystem.debug('SOQL Queries Used: ' + Limits.getQueries() + '/' + Limits.getLimitQueries());\nSystem.debug('DML Statements: ' + Limits.getDmlStatements() + '/' + Limits.getLimitDmlStatements());\n```\n\nSave as `bulk-test-accounts.apex` and run:\n```bash\nsf apex run --file bulk-test-accounts.apex --target-org dev\n```\n\n## Method 2: CSV Bulk Import\n\n### Create CSV File\n```csv\nName,Industry,AnnualRevenue\nBulkTest Account 1,Technology,1000000\nBulkTest Account 2,Healthcare,2000000\nBulkTest Account 3,Finance,5000000\n... (251 rows)\n```\n\n### Import via sf CLI\n```bash\nsf data import bulk \\\n --file accounts-bulk.csv \\\n --sobject Account \\\n --target-org dev \\\n --wait 30\n```\n\n## Method 3: JSON Tree Import\n\nFor hierarchical test data with relationships:\n\n```json\n{\n \"records\": [\n {\n \"attributes\": {\"type\": \"Account\", \"referenceId\": \"AccountRef1\"},\n \"Name\": \"BulkTest Parent 1\",\n \"Industry\": \"Technology\",\n \"Contacts\": {\n \"records\": [\n {\n \"attributes\": {\"type\": \"Contact\"},\n \"FirstName\": \"Test\",\n \"LastName\": \"Contact 1\"\n }\n ]\n }\n }\n ]\n}\n```\n\n```bash\nsf data import tree \\\n --files bulk-hierarchy.json \\\n --target-org dev\n```\n\n## Verify Trigger Executed Correctly\n\n### Check Trigger Results\n```bash\nsf data query \\\n --query \"SELECT Id, Name, Industry, Custom_Field__c FROM Account WHERE Name LIKE 'BulkTest%' LIMIT 10\" \\\n --target-org dev \\\n --json\n```\n\n### Check Related Tasks Created\n```bash\nsf data query \\\n --query \"SELECT Id, Subject, WhatId, What.Name FROM Task WHERE What.Name LIKE 'BulkTest%'\" \\\n --target-org dev\n```\n\n### Count Records\n```bash\nsf data query \\\n --query \"SELECT COUNT(Id) total FROM Account WHERE Name LIKE 'BulkTest%'\" \\\n --target-org dev\n```\n\n## Test Bulk Update\n\n```apex\n// Update all test records - triggers fire again\nList\u003cAccount> accounts = [\n SELECT Id, Name, Industry\n FROM Account\n WHERE Name LIKE 'BulkTest%'\n];\n\nfor (Account acc : accounts) {\n acc.Description = 'Bulk updated on ' + DateTime.now();\n}\n\nupdate accounts;\n\nSystem.debug('Updated ' + accounts.size() + ' accounts');\n```\n\n## Test Bulk Delete\n\n```apex\n// Delete in reverse order (children first)\nList\u003cTask> tasks = [SELECT Id FROM Task WHERE What.Name LIKE 'BulkTest%'];\ndelete tasks;\n\nList\u003cContact> contacts = [SELECT Id FROM Contact WHERE Account.Name LIKE 'BulkTest%'];\ndelete contacts;\n\nList\u003cAccount> accounts = [SELECT Id FROM Account WHERE Name LIKE 'BulkTest%'];\ndelete accounts;\n\nSystem.debug('Cleanup complete');\n```\n\n## Governor Limits Monitoring\n\n```apex\n// Add to your test script to monitor limits\nSystem.debug('=== GOVERNOR LIMITS ===');\nSystem.debug('SOQL: ' + Limits.getQueries() + '/' + Limits.getLimitQueries());\nSystem.debug('DML Statements: ' + Limits.getDmlStatements() + '/' + Limits.getLimitDmlStatements());\nSystem.debug('DML Rows: ' + Limits.getDmlRows() + '/' + Limits.getLimitDmlRows());\nSystem.debug('CPU Time: ' + Limits.getCpuTime() + 'ms/' + Limits.getLimitCpuTime() + 'ms');\nSystem.debug('Heap: ' + Limits.getHeapSize() + '/' + Limits.getLimitHeapSize());\n```\n\n## Validation Score\n\n```\nScore: 128/130 ⭐⭐⭐⭐⭐ Excellent\n├─ Query Efficiency: 25/25 (bulk queries)\n├─ Bulk Safety: 25/25 (251 records, limits monitored)\n├─ Data Integrity: 20/20 (valid field values)\n├─ Security & FLS: 20/20 (no sensitive data)\n├─ Test Patterns: 15/15 (201+ records, variations)\n├─ Cleanup & Isolation: 13/15 (cleanup script provided)\n└─ Documentation: 10/10 (fully documented)\n```\n\n## Common Issues\n\n| Issue | Cause | Solution |\n|-------|-------|----------|\n| `SOQL 101` | Query in loop | Use Map or Set for bulk queries |\n| `DML 151` | DML in loop | Collect records, single DML |\n| `CPU timeout` | Complex logic | Optimize loops, async processing |\n| `Too many records` | >10,000 DML rows | Use Bulk API or Batch Apex |\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":5032,"content_sha256":"1440c8887fde44de2300a23dee1c9614310c27a82a59d77c2c9b7c1dfec430b6"},{"filename":"references/cleanup-rollback-example.md","content":"\u003c!-- Parent: sf-data/SKILL.md -->\n# Cleanup and Rollback Examples\n\nStrategies for test data isolation and proper cleanup.\n\n## Method 1: Savepoint/Rollback\n\nBest for synchronous operations in a single transaction.\n\n### Basic Pattern\n```apex\n// Create savepoint BEFORE any DML\nSavepoint sp = Database.setSavepoint();\n\ntry {\n // Create test data\n Account acc = new Account(Name = 'Test Account');\n insert acc;\n\n Contact con = new Contact(\n FirstName = 'Test',\n LastName = 'Contact',\n AccountId = acc.Id\n );\n insert con;\n\n // Perform operations\n MyClass.processAccount(acc);\n\n // Assert results\n acc = [SELECT Custom_Field__c FROM Account WHERE Id = :acc.Id];\n System.assert(acc.Custom_Field__c != null, 'Field should be populated');\n\n} finally {\n // ALWAYS rollback - even on success\n Database.rollback(sp);\n}\n\n// Data is now completely removed\n```\n\n### In Unit Tests\n```apex\n@isTest\nstatic void testWithRollback() {\n Savepoint sp = Database.setSavepoint();\n\n try {\n // Create 1000 test records\n List\u003cAccount> accounts = new List\u003cAccount>();\n for (Integer i = 0; i \u003c 1000; i++) {\n accounts.add(new Account(Name = 'Rollback Test ' + i));\n }\n insert accounts;\n\n // Run business logic\n Test.startTest();\n BatchProcessor.process(accounts);\n Test.stopTest();\n\n // Assertions\n Integer count = [SELECT COUNT() FROM Account WHERE Name LIKE 'Rollback Test%'];\n System.assertEquals(1000, count);\n\n } finally {\n Database.rollback(sp);\n }\n\n // Verify cleanup\n Integer remaining = [SELECT COUNT() FROM Account WHERE Name LIKE 'Rollback Test%'];\n System.assertEquals(0, remaining, 'All records should be rolled back');\n}\n```\n\n### Limitations\n- **Does NOT rollback async operations** (future, queueable, batch)\n- Maximum 5 savepoints per transaction\n- Cannot rollback across transaction boundaries\n\n## Method 2: Cleanup by Name Pattern\n\nBest when savepoint isn't possible (async tests, multi-transaction).\n\n### Single Object Cleanup\n```apex\n// Delete all test accounts\nList\u003cAccount> toDelete = [\n SELECT Id FROM Account\n WHERE Name LIKE 'Test%'\n LIMIT 10000\n];\ndelete toDelete;\n```\n\n### Multi-Object Cleanup (Correct Order)\n```apex\n// CRITICAL: Delete children before parents!\nString testPattern = 'Test%';\n\n// 1. Delete grandchildren first\ndelete [SELECT Id FROM Task WHERE What.Name LIKE :testPattern];\ndelete [SELECT Id FROM Event WHERE What.Name LIKE :testPattern];\n\n// 2. Delete children\ndelete [SELECT Id FROM Opportunity WHERE Account.Name LIKE :testPattern];\ndelete [SELECT Id FROM Contact WHERE Account.Name LIKE :testPattern];\ndelete [SELECT Id FROM Case WHERE Account.Name LIKE :testPattern];\n\n// 3. Delete parents last\ndelete [SELECT Id FROM Account WHERE Name LIKE :testPattern];\n\nSystem.debug('Cleanup complete');\n```\n\n### sf CLI Cleanup\n```bash\n# Query records to delete\nsf data query \\\n --query \"SELECT Id FROM Account WHERE Name LIKE 'Test%'\" \\\n --target-org dev \\\n --result-format csv \\\n > delete-accounts.csv\n\n# Bulk delete\nsf data delete bulk \\\n --file delete-accounts.csv \\\n --sobject Account \\\n --target-org dev \\\n --wait 30\n```\n\n## Method 3: Cleanup by Time Window\n\nBest for cleaning up after a specific test run.\n\n### By CreatedDate\n```apex\n// Clean up records created in the last hour\nDateTime startTime = DateTime.now().addHours(-1);\nString testPattern = 'Test%';\n\ndelete [\n SELECT Id FROM Account\n WHERE CreatedDate >= :startTime\n AND Name LIKE :testPattern\n];\n```\n\n### With Timestamp Tracking\n```apex\n// At start of test - capture timestamp\nDateTime testStartTime = DateTime.now();\n\n// ... run tests ...\n\n// Cleanup - only records created during this test\ndelete [\n SELECT Id FROM Account\n WHERE CreatedDate >= :testStartTime\n AND Name LIKE 'Test%'\n];\n```\n\n## Method 4: ID-Based Cleanup\n\nMost precise - track exactly what you created.\n\n### Track IDs During Creation\n```apex\n// Collection to track all created record IDs\nSet\u003cId> createdAccountIds = new Set\u003cId>();\nSet\u003cId> createdContactIds = new Set\u003cId>();\nSet\u003cId> createdOppIds = new Set\u003cId>();\n\n// Create and track\nList\u003cAccount> accounts = TestDataFactory_Account.create(100);\ncreatedAccountIds.addAll(new Map\u003cId, Account>(accounts).keySet());\n\nList\u003cContact> contacts = TestDataFactory_Contact.createForAccounts(createdAccountIds, 3);\ncreatedContactIds.addAll(new Map\u003cId, Contact>(contacts).keySet());\n\n// ... run tests ...\n\n// Cleanup exactly what we created\ndelete [SELECT Id FROM Contact WHERE Id IN :createdContactIds];\ndelete [SELECT Id FROM Account WHERE Id IN :createdAccountIds];\n```\n\n### Wrapper Class for Tracking\n```apex\npublic class TestDataTracker {\n private Map\u003cString, Set\u003cId>> trackedIds = new Map\u003cString, Set\u003cId>>();\n\n public void track(SObject record) {\n String objType = record.getSObjectType().getDescribe().getName();\n if (!trackedIds.containsKey(objType)) {\n trackedIds.put(objType, new Set\u003cId>());\n }\n trackedIds.get(objType).add(record.Id);\n }\n\n public void trackAll(List\u003cSObject> records) {\n for (SObject rec : records) {\n track(rec);\n }\n }\n\n public void cleanup() {\n // Delete in reverse dependency order\n List\u003cString> deleteOrder = new List\u003cString>{\n 'Task', 'Event', 'Opportunity', 'Contact', 'Case', 'Account'\n };\n\n for (String objType : deleteOrder) {\n if (trackedIds.containsKey(objType)) {\n Set\u003cId> ids = trackedIds.get(objType);\n String query = 'SELECT Id FROM ' + objType + ' WHERE Id IN :ids';\n delete Database.query(query);\n }\n }\n }\n}\n\n// Usage\nTestDataTracker tracker = new TestDataTracker();\n\nList\u003cAccount> accounts = TestDataFactory_Account.create(100);\ntracker.trackAll(accounts);\n\nList\u003cContact> contacts = TestDataFactory_Contact.create(50);\ntracker.trackAll(contacts);\n\n// ... run tests ...\n\n// Clean up everything tracked\ntracker.cleanup();\n```\n\n## Method 5: @testSetup Isolation\n\nAutomatic rollback with @isTest annotation.\n\n```apex\n@isTest\npublic class MyTestClass {\n\n @testSetup\n static void setup() {\n // This data is automatically rolled back after ALL tests complete\n List\u003cAccount> accounts = TestDataFactory_Account.create(100);\n }\n\n @isTest\n static void testMethod1() {\n // Access setup data\n List\u003cAccount> accounts = [SELECT Id FROM Account];\n // Modify data - changes rolled back after this test\n delete accounts[0];\n }\n\n @isTest\n static void testMethod2() {\n // Fresh copy of @testSetup data - all 100 accounts available\n List\u003cAccount> accounts = [SELECT Id FROM Account];\n System.assertEquals(100, accounts.size());\n }\n}\n```\n\n## Cleanup via sf CLI\n\n### Generate Cleanup CSV\n```bash\n# Export IDs of test records\nsf data query \\\n --query \"SELECT Id FROM Account WHERE Name LIKE 'Test%'\" \\\n --target-org dev \\\n --result-format csv \\\n > cleanup-accounts.csv\n\nsf data query \\\n --query \"SELECT Id FROM Contact WHERE Account.Name LIKE 'Test%'\" \\\n --target-org dev \\\n --result-format csv \\\n > cleanup-contacts.csv\n```\n\n### Execute Bulk Delete\n```bash\n# Delete children first\nsf data delete bulk \\\n --file cleanup-contacts.csv \\\n --sobject Contact \\\n --target-org dev \\\n --wait 30\n\n# Then delete parents\nsf data delete bulk \\\n --file cleanup-accounts.csv \\\n --sobject Account \\\n --target-org dev \\\n --wait 30\n```\n\n## Best Practices Summary\n\n| Method | Best For | Limitations |\n|--------|----------|-------------|\n| Savepoint/Rollback | Synchronous tests | No async, max 5 savepoints |\n| Name Pattern | Ad-hoc cleanup | May delete unintended records |\n| Time Window | Post-test cleanup | Needs accurate timestamp |\n| ID Tracking | Precise cleanup | Requires tracking discipline |\n| @testSetup | Unit tests | Only in @isTest classes |\n| sf CLI Bulk | Large volumes | External tool required |\n\n## Golden Rules\n\n1. **Always delete children before parents** - Respect relationships\n2. **Use specific patterns** - 'Test%', 'BulkTest%' to avoid accidents\n3. **Verify before delete** - Query first to see what will be deleted\n4. **Test cleanup in sandbox** - Never run unverified cleanup in prod\n5. **Track created IDs** - Most reliable cleanup method\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":8373,"content_sha256":"ab270d1317fc932e2c70096057824f9cca86760ecad27a624d7fa5103b2d1c53"},{"filename":"references/cleanup-rollback-guide.md","content":"\u003c!-- Parent: sf-data/SKILL.md -->\n# Cleanup and Rollback Guide\n\nStrategies for test data isolation and cleanup.\n\n## Savepoint/Rollback Pattern\n\nBest for synchronous test isolation.\n\n```apex\n// Create savepoint BEFORE any DML\nSavepoint sp = Database.setSavepoint();\n\ntry {\n // Create test data\n List\u003cAccount> accounts = TestDataFactory_Account.create(100);\n\n // Run your tests\n Test.startTest();\n MyClass.processAccounts(accounts);\n Test.stopTest();\n\n // Assert results\n System.assertEquals(expected, actual);\n\n} finally {\n // Always rollback\n Database.rollback(sp);\n}\n```\n\n**Limitations:**\n- Does not roll back async operations\n- Maximum 5 savepoints per transaction\n\n## Cleanup by Name Pattern\n\n```apex\nString pattern = 'Test%';\n\nDELETE [SELECT Id FROM Opportunity WHERE Name LIKE :pattern];\nDELETE [SELECT Id FROM Contact WHERE LastName LIKE :pattern];\nDELETE [SELECT Id FROM Account WHERE Name LIKE :pattern];\n```\n\n**Order matters:** Delete children before parents.\n\n## Cleanup by Date\n\n```apex\nDateTime startTime = DateTime.now().addHours(-1);\n\nDELETE [\n SELECT Id FROM Account\n WHERE CreatedDate >= :startTime\n AND Name LIKE 'Test%'\n];\n```\n\n## Cleanup via sf CLI\n\n```bash\n# Export IDs to delete\nsf data query \\\n --query \"SELECT Id FROM Account WHERE Name LIKE 'Test%'\" \\\n --target-org myorg \\\n --result-format csv \\\n > delete.csv\n\n# Bulk delete\nsf data delete bulk \\\n --file delete.csv \\\n --sobject Account \\\n --target-org myorg \\\n --wait 30\n```\n\n## Best Practices\n\n1. **Track created IDs** - Store in Set\u003cId>\n2. **Delete in order** - Children first, parents last\n3. **Use test prefixes** - 'Test', 'BulkTest'\n4. **Preview before delete** - Verify records first\n5. **Use @isTest** - Auto-rollback in tests\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":1758,"content_sha256":"c5754f550b383beec23bcd8b5aab089dae8e15dffee5a21aab31d5bb24e933d6"},{"filename":"references/crud-workflow-example.md","content":"\u003c!-- Parent: sf-data/SKILL.md -->\n# CRUD Workflow Example\n\nComplete end-to-end example of data operations using sf-data skill.\n\n## Scenario\n\nCreate a Deal Desk workflow test environment with:\n- Accounts with varying revenue tiers\n- Contacts as decision makers\n- Opportunities at different stages\n\n## Phase 1: Discovery (sf-metadata)\n\n```\nSkill(skill=\"sf-metadata\")\nRequest: \"Describe object Account in org dev - show required fields and picklist values\"\n```\n\n**Response shows:**\n- Required: Name\n- Picklists: Industry, Type, Rating\n\n## Phase 2: Create Records\n\n### sf CLI - Single Record\n```bash\nsf data create record \\\n --sobject Account \\\n --values \"Name='Enterprise Corp' Industry='Technology' AnnualRevenue=5000000\" \\\n --target-org dev \\\n --json\n```\n\n**Output:**\n```json\n{\n \"status\": 0,\n \"result\": {\n \"id\": \"001XXXXXXXXXXXX\",\n \"success\": true\n }\n}\n```\n\n### sf CLI - Query Created Record\n```bash\nsf data query \\\n --query \"SELECT Id, Name, Industry, AnnualRevenue FROM Account WHERE Name = 'Enterprise Corp'\" \\\n --target-org dev \\\n --json\n```\n\n## Phase 3: Update Records\n\n### Update Single Record\n```bash\nsf data update record \\\n --sobject Account \\\n --record-id 001XXXXXXXXXXXX \\\n --values \"Rating='Hot' Type='Customer - Direct'\" \\\n --target-org dev\n```\n\n### Verify Update\n```bash\nsf data get record \\\n --sobject Account \\\n --record-id 001XXXXXXXXXXXX \\\n --target-org dev\n```\n\n## Phase 4: Create Related Records\n\n### Create Contact for Account\n```bash\nsf data create record \\\n --sobject Contact \\\n --values \"FirstName='John' LastName='Smith' AccountId='001XXXXXXXXXXXX' Title='CTO'\" \\\n --target-org dev\n```\n\n### Create Opportunity\n```bash\nsf data create record \\\n --sobject Opportunity \\\n --values \"Name='Enterprise Deal' AccountId='001XXXXXXXXXXXX' StageName='Prospecting' CloseDate=2025-03-31 Amount=250000\" \\\n --target-org dev\n```\n\n## Phase 5: Query Relationships\n\n### Parent-to-Child (Subquery)\n```bash\nsf data query \\\n --query \"SELECT Id, Name, (SELECT Id, Name, Title FROM Contacts), (SELECT Id, Name, Amount, StageName FROM Opportunities) FROM Account WHERE Name = 'Enterprise Corp'\" \\\n --target-org dev \\\n --json\n```\n\n### Child-to-Parent (Dot Notation)\n```bash\nsf data query \\\n --query \"SELECT Id, Name, Account.Name, Account.Industry FROM Contact WHERE Account.Name = 'Enterprise Corp'\" \\\n --target-org dev\n```\n\n## Phase 6: Delete Records\n\n### Delete in Correct Order\nChildren first, then parents:\n\n```bash\n# Delete Opportunities\nsf data delete record \\\n --sobject Opportunity \\\n --record-id 006XXXXXXXXXXXX \\\n --target-org dev\n\n# Delete Contacts\nsf data delete record \\\n --sobject Contact \\\n --record-id 003XXXXXXXXXXXX \\\n --target-org dev\n\n# Delete Account\nsf data delete record \\\n --sobject Account \\\n --record-id 001XXXXXXXXXXXX \\\n --target-org dev\n```\n\n## Anonymous Apex Alternative\n\nFor complex operations, use Anonymous Apex:\n\n```apex\n// Create complete hierarchy in one transaction\nAccount acc = new Account(\n Name = 'Enterprise Corp',\n Industry = 'Technology',\n AnnualRevenue = 5000000\n);\ninsert acc;\n\nContact con = new Contact(\n FirstName = 'John',\n LastName = 'Smith',\n AccountId = acc.Id,\n Title = 'CTO'\n);\ninsert con;\n\nOpportunity opp = new Opportunity(\n Name = 'Enterprise Deal',\n AccountId = acc.Id,\n ContactId = con.Id,\n StageName = 'Prospecting',\n CloseDate = Date.today().addDays(90),\n Amount = 250000\n);\ninsert opp;\n\nSystem.debug('Created hierarchy: Account=' + acc.Id + ', Contact=' + con.Id + ', Opp=' + opp.Id);\n```\n\nExecute:\n```bash\nsf apex run --file create-hierarchy.apex --target-org dev\n```\n\n## Validation Score\n\n```\nScore: 125/130 ⭐⭐⭐⭐⭐ Excellent\n├─ Query Efficiency: 25/25 (indexed fields, no N+1)\n├─ Bulk Safety: 23/25 (single records OK for demo)\n├─ Data Integrity: 20/20 (all required fields)\n├─ Security & FLS: 20/20 (no PII exposed)\n├─ Test Patterns: 12/15 (single record demo)\n├─ Cleanup & Isolation: 15/15 (proper delete order)\n└─ Documentation: 10/10 (fully documented)\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":4043,"content_sha256":"d8058f222f62a028a9519dc8c1ec79cd59df791048930c8fffb2e89ea0630cba"},{"filename":"references/governor-limits-reference.md","content":"\u003c!-- Parent: sf-data/SKILL.md -->\n# Governor Limits Reference\n\nEssential limits for Salesforce data operations.\n\n## SOQL Limits\n\n| Limit | Synchronous | Asynchronous |\n|-------|-------------|--------------|\n| Total queries | 100 | 200 |\n| Rows retrieved | 50,000 | 50,000 |\n| QueryLocator rows | N/A | 50,000,000 |\n| Query timeout | 120 seconds | 120 seconds |\n\n## DML Limits\n\n| Limit | Synchronous | Asynchronous |\n|-------|-------------|--------------|\n| DML statements | 150 | 150 |\n| Rows processed | 10,000 | 10,000 |\n\n## CPU and Memory\n\n| Limit | Synchronous | Asynchronous |\n|-------|-------------|--------------|\n| CPU time | 10,000 ms | 60,000 ms |\n| Heap size | 6 MB | 12 MB |\n\n## Bulk API Limits\n\n| Limit | Value |\n|-------|-------|\n| Batches per 24 hours | 10,000 |\n| Records per 24 hours | 10,000,000 |\n| Max file size | 100 MB |\n| Concurrent jobs | 100 |\n\n## Staying Within Limits\n\n### SOQL Best Practices\n```apex\n// BAD: Query in loop\nfor (Account acc : accounts) {\n List\u003cContact> cons = [SELECT Id FROM Contact WHERE AccountId = :acc.Id]; // ❌\n}\n\n// GOOD: Single query with relationship\nList\u003cAccount> accs = [\n SELECT Id, (SELECT Id FROM Contacts)\n FROM Account\n WHERE Id IN :accountIds\n]; // ✓\n```\n\n### DML Best Practices\n```apex\n// BAD: DML in loop\nfor (Account acc : accounts) {\n update acc; // ❌\n}\n\n// GOOD: Bulk DML\nupdate accounts; // ✓\n```\n\n## Monitoring Limits\n\n```apex\nSystem.debug('SOQL Queries: ' + Limits.getQueries() + '/' + Limits.getLimitQueries());\nSystem.debug('DML Statements: ' + Limits.getDmlStatements() + '/' + Limits.getLimitDmlStatements());\nSystem.debug('DML Rows: ' + Limits.getDmlRows() + '/' + Limits.getLimitDmlRows());\nSystem.debug('CPU Time: ' + Limits.getCpuTime() + '/' + Limits.getLimitCpuTime());\nSystem.debug('Heap Size: ' + Limits.getHeapSize() + '/' + Limits.getLimitHeapSize());\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":1865,"content_sha256":"9b9e160d9077097afccee6a7a9fe7160436b01d69f61920e6a853ef94fd9e7dd"},{"filename":"references/orchestration.md","content":"\u003c!-- Parent: sf-data/SKILL.md -->\n# Multi-Skill Orchestration: sf-data Perspective\n\nThis document details how sf-data fits into the multi-skill workflow for Salesforce development.\n\n---\n\n## Standard Orchestration Order\n\n```\n┌─────────────────────────────────────────────────────────────────────────────┐\n│ STANDARD MULTI-SKILL ORCHESTRATION ORDER │\n├─────────────────────────────────────────────────────────────────────────────┤\n│ 1. sf-metadata │\n│ └── Create object/field definitions (LOCAL files) │\n│ │\n│ 2. sf-flow │\n│ └── Create flow definitions (LOCAL files) │\n│ │\n│ 3. sf-deploy │\n│ └── Deploy all metadata (REMOTE) │\n│ │\n│ 4. sf-data ◀── YOU ARE HERE (LAST!) │\n│ └── Create test data (REMOTE - objects must exist!) │\n└─────────────────────────────────────────────────────────────────────────────┘\n```\n\n---\n\n## ⚠️ Why sf-data Goes LAST\n\n**sf-data operates on REMOTE org data.** Objects/fields must be deployed before sf-data can:\n- Insert records\n- Query existing data\n- Run test factories\n- Generate bulk test data\n\n```\nERROR: \"SObject type 'Quote__c' is not supported\"\nCAUSE: Quote__c object was never deployed to the org\nFIX: Run sf-deploy BEFORE sf-data\n```\n\n---\n\n## Common Errors from Wrong Order\n\n| Error | Cause | Fix |\n|-------|-------|-----|\n| `SObject type 'X' not supported` | Object not deployed | Deploy via sf-deploy first |\n| `INVALID_FIELD: No such column 'Field__c'` | Field not deployed OR FLS | Deploy field + Permission Set |\n| `REQUIRED_FIELD_MISSING` | Validation rule requires field | Include all required fields |\n| `FIELD_CUSTOM_VALIDATION_EXCEPTION` | Validation rule triggered | Use valid test data values |\n\n---\n\n## Test Data After Triggers/Flows\n\nWhen testing triggers or flows, always create test data AFTER deployment:\n\n```\n1. sf-apex → Create trigger handler class\n2. sf-flow → Create record-triggered flow\n3. sf-deploy → Deploy trigger + flow + objects\n4. sf-data ◀── CREATE TEST DATA NOW\n └── Triggers and flows will fire!\n```\n\n**Why?** Test data insertion triggers flows/triggers. If those aren't deployed, you're not testing realistic behavior.\n\n---\n\n## The 251-Record Pattern\n\nAlways test with **251 records** to cross the 200-record batch boundary:\n\n```\n┌─────────────────────────────────────────────────────────────────────────────┐\n│ BATCH BOUNDARY TESTING │\n├─────────────────────────────────────────────────────────────────────────────┤\n│ Records 1-200: First batch │\n│ Records 201-251: Second batch (crosses boundary!) │\n│ │\n│ Tests: N+1 queries, bulkification, governor limits │\n└─────────────────────────────────────────────────────────────────────────────┘\n```\n\n**Command:**\n```bash\nsf apex run --file test-factory.apex --target-org alias\n# test-factory.apex creates 251 records\n```\n\n---\n\n## Cross-Skill Integration Table\n\n| From Skill | To sf-data | When |\n|------------|------------|------|\n| sf-apex | → sf-data | \"Create 251 Accounts for bulk testing\" |\n| sf-flow | → sf-data | \"Create Opportunities with StageName='Closed Won'\" |\n| sf-testing | → sf-data | \"Generate test records for test class\" |\n\n| From sf-data | To Skill | When |\n|--------------|----------|------|\n| sf-data | → sf-metadata | \"Describe Invoice__c\" (discover object structure) |\n| sf-data | → sf-deploy | \"Redeploy field after adding validation rule\" |\n\n---\n\n## Prerequisites Check\n\nBefore using sf-data, verify:\n\n```bash\n# Check org connection\nsf org display --target-org alias\n\n# Check objects exist\nsf sobject describe --sobject MyObject__c --target-org alias --json\n\n# Check field-level security (if field not visible)\nsf data query --query \"SELECT Id FROM FieldPermissions WHERE SobjectType='MyObject__c'\" --use-tooling-api --target-org alias\n```\n\n---\n\n## Factory Pattern Integration\n\nTest Data Factory classes work with sf-data:\n\n```\nsf-apex: Creates TestDataFactory_Account.cls\n ↓\nsf-deploy: Deploys factory class\n ↓\nsf-data: Calls factory via Anonymous Apex\n ↓\n 251 records created → triggers fire → flows run\n```\n\n**Anonymous Apex:**\n```apex\nList\u003cAccount> accounts = TestDataFactory_Account.create(251);\nSystem.debug('Created ' + accounts.size() + ' accounts');\n```\n\n---\n\n## Cleanup Sequence\n\nAfter testing, clean up in reverse order:\n\n```\n1. sf-data → Delete test records\n2. sf-deploy → Deactivate flows (if needed)\n3. sf-deploy → Remove test metadata (if needed)\n```\n\n**Cleanup command:**\n```bash\nsf apex run --file cleanup.apex --target-org alias\n# cleanup.apex: DELETE [SELECT Id FROM Account WHERE Name LIKE 'Test%']\n```\n\n---\n\n## Related Documentation\n\n| Topic | Location |\n|-------|----------|\n| Test data patterns | `sf-data/references/test-data-patterns.md` |\n| Cleanup guide | `sf-data/references/cleanup-rollback-guide.md` |\n| Factory templates | `sf-data/assets/factories/` |\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":6726,"content_sha256":"a881ae0749cf1e37e6c4f7fb52537ca282954fc6c7a7451d1d9843a4fdd91b51"},{"filename":"references/relationship-query-examples.md","content":"\u003c!-- Parent: sf-data/SKILL.md -->\n# Relationship Query Examples\n\nComprehensive SOQL relationship patterns for complex data retrieval.\n\n## Parent-to-Child Queries (Subqueries)\n\n### Basic Subquery\n```sql\nSELECT Id, Name,\n (SELECT Id, FirstName, LastName, Email FROM Contacts)\nFROM Account\nWHERE Industry = 'Technology'\n```\n\n```bash\nsf data query \\\n --query \"SELECT Id, Name, (SELECT Id, FirstName, LastName FROM Contacts) FROM Account WHERE Industry = 'Technology' LIMIT 5\" \\\n --target-org dev \\\n --json\n```\n\n### Multiple Subqueries\n```sql\nSELECT Id, Name,\n (SELECT Id, Name, Title FROM Contacts),\n (SELECT Id, Name, Amount, StageName FROM Opportunities),\n (SELECT Id, CaseNumber, Subject FROM Cases)\nFROM Account\nWHERE Id = '001XXXXXXXXXXXX'\n```\n\n### Filtered Subquery\n```sql\nSELECT Id, Name,\n (SELECT Id, Name, Amount\n FROM Opportunities\n WHERE StageName = 'Closed Won'\n ORDER BY Amount DESC\n LIMIT 5)\nFROM Account\nWHERE AnnualRevenue > 1000000\n```\n\n## Child-to-Parent Queries (Dot Notation)\n\n### Basic Relationship\n```sql\nSELECT Id, FirstName, LastName,\n Account.Name, Account.Industry\nFROM Contact\nWHERE Account.Industry = 'Technology'\n```\n\n```bash\nsf data query \\\n --query \"SELECT Id, FirstName, LastName, Account.Name, Account.Industry FROM Contact WHERE Account.Industry = 'Technology' LIMIT 10\" \\\n --target-org dev\n```\n\n### Multi-Level Traversal\n```sql\nSELECT Id, Name, Amount,\n Account.Name,\n Account.Owner.Name,\n Account.Owner.Profile.Name\nFROM Opportunity\nWHERE Account.Industry = 'Finance'\n```\n\n### Custom Object Relationships\n```sql\n-- __r suffix for custom relationships\nSELECT Id, Name,\n Custom_Parent__r.Name,\n Custom_Parent__r.Custom_Field__c\nFROM Custom_Child__c\n```\n\n## Polymorphic Relationships (TYPEOF)\n\n### Task Who/What Fields\n```sql\nSELECT Id, Subject, Status,\n TYPEOF Who\n WHEN Contact THEN FirstName, LastName, Email\n WHEN Lead THEN FirstName, LastName, Company\n END,\n TYPEOF What\n WHEN Account THEN Name, Industry\n WHEN Opportunity THEN Name, Amount, StageName\n END\nFROM Task\nWHERE Status = 'Open'\n```\n\n```bash\nsf data query \\\n --query \"SELECT Id, Subject, TYPEOF Who WHEN Contact THEN FirstName, LastName WHEN Lead THEN FirstName, LastName END FROM Task WHERE Status = 'Open' LIMIT 5\" \\\n --target-org dev \\\n --json\n```\n\n### Event Relationships\n```sql\nSELECT Id, Subject, StartDateTime,\n TYPEOF Who\n WHEN Contact THEN FirstName, LastName, Account.Name\n WHEN Lead THEN Name, Company\n END\nFROM Event\nWHERE StartDateTime >= TODAY\n```\n\n## Aggregate Queries\n\n### Basic Aggregation\n```sql\nSELECT Industry, COUNT(Id) total, SUM(AnnualRevenue) revenue\nFROM Account\nGROUP BY Industry\nHAVING COUNT(Id) > 10\nORDER BY SUM(AnnualRevenue) DESC\n```\n\n```bash\nsf data query \\\n --query \"SELECT Industry, COUNT(Id) total, SUM(AnnualRevenue) revenue FROM Account GROUP BY Industry HAVING COUNT(Id) > 5\" \\\n --target-org dev\n```\n\n### Rollup by Date\n```sql\nSELECT CALENDAR_MONTH(CloseDate) month,\n SUM(Amount) total,\n COUNT(Id) deals\nFROM Opportunity\nWHERE CloseDate = THIS_YEAR\nAND StageName = 'Closed Won'\nGROUP BY CALENDAR_MONTH(CloseDate)\nORDER BY CALENDAR_MONTH(CloseDate)\n```\n\n### Aggregate with Relationships\n```sql\nSELECT Account.Industry,\n COUNT(Id) opportunities,\n SUM(Amount) pipeline\nFROM Opportunity\nWHERE StageName NOT IN ('Closed Won', 'Closed Lost')\nGROUP BY Account.Industry\n```\n\n## Semi-Joins and Anti-Joins\n\n### Semi-Join (Records WITH Related)\n```sql\n-- Accounts that HAVE Opportunities\nSELECT Id, Name\nFROM Account\nWHERE Id IN (\n SELECT AccountId FROM Opportunity WHERE Amount > 100000\n)\n```\n\n### Anti-Join (Records WITHOUT Related)\n```sql\n-- Accounts WITHOUT Contacts\nSELECT Id, Name\nFROM Account\nWHERE Id NOT IN (\n SELECT AccountId FROM Contact WHERE AccountId != null\n)\n```\n\n### Complex Semi-Join\n```sql\n-- Contacts at Accounts with large deals\nSELECT Id, FirstName, LastName\nFROM Contact\nWHERE AccountId IN (\n SELECT AccountId\n FROM Opportunity\n WHERE Amount > 500000\n AND StageName = 'Closed Won'\n)\n```\n\n## Date Functions and Filters\n\n### Date Literals\n```sql\nSELECT Id, Name, CreatedDate\nFROM Account\nWHERE CreatedDate = THIS_WEEK\n-- Also: TODAY, YESTERDAY, THIS_MONTH, LAST_MONTH, THIS_YEAR, LAST_N_DAYS:7\n```\n\n### Date Functions in SELECT\n```sql\nSELECT DAY_IN_MONTH(CreatedDate) day,\n WEEK_IN_YEAR(CreatedDate) week,\n CALENDAR_YEAR(CreatedDate) year,\n COUNT(Id) count\nFROM Lead\nGROUP BY DAY_IN_MONTH(CreatedDate),\n WEEK_IN_YEAR(CreatedDate),\n CALENDAR_YEAR(CreatedDate)\n```\n\n## Query Optimization Tips\n\n### Use Indexed Fields\n```sql\n-- GOOD: Query on indexed fields (Id, Name, CreatedDate, SystemModstamp)\nSELECT Id, Name FROM Account WHERE Name LIKE 'Acme%'\n\n-- BAD: Query on non-indexed custom field (full table scan)\nSELECT Id, Name FROM Account WHERE Custom_Unindexed__c = 'value'\n```\n\n### Limit Records Retrieved\n```sql\n-- Always use LIMIT for exploration\nSELECT Id, Name FROM Account LIMIT 100\n\n-- Use OFFSET for pagination\nSELECT Id, Name FROM Account LIMIT 100 OFFSET 200\n```\n\n### Select Only Needed Fields\n```sql\n-- GOOD: Only needed fields\nSELECT Id, Name, Industry FROM Account\n\n-- BAD: Selecting all fields\nSELECT Id, Name, Industry, Description, BillingStreet, BillingCity,\n BillingState, BillingPostalCode, BillingCountry, ...\n```\n\n## Common Errors\n\n| Error | Cause | Solution |\n|-------|-------|----------|\n| `Invalid relationship` | Wrong relationship name | Check `__r` suffix for custom |\n| `MALFORMED_QUERY` | Syntax error | Validate SOQL syntax |\n| `SELECT too complex` | Too many levels | Max 5 levels of relationships |\n| `Subquery limit` | >20 subqueries | Reduce number of child queries |\n| `Non-selective query` | No indexed filter | Add indexed field to WHERE |\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":5838,"content_sha256":"a70bfae9e8cd923f0ad513a907927d56df3bb649bb604f9dabee7d6d51ac5535"},{"filename":"references/sf-cli-data-commands.md","content":"\u003c!-- Parent: sf-data/SKILL.md -->\n# sf CLI Data Commands Reference\n\nComplete reference for Salesforce CLI v2 data commands.\n\n## Query Commands\n\n### SOQL Query\n```bash\nsf data query \\\n --query \"SELECT Id, Name FROM Account LIMIT 10\" \\\n --target-org myorg \\\n --json\n```\n\n### SOSL Search\n```bash\nsf data search \\\n --query \"FIND {Acme}\" \\\n --target-org myorg\n```\n\n### Bulk Export\n```bash\nsf data export bulk \\\n --query \"SELECT Id, Name FROM Account\" \\\n --output-file accounts.csv \\\n --target-org myorg \\\n --wait 30\n```\n\n## Record Operations\n\n### Create Record\n```bash\nsf data create record \\\n --sobject Account \\\n --values \"Name='Acme' Industry='Technology'\" \\\n --target-org myorg\n```\n\n### Update Record\n```bash\nsf data update record \\\n --sobject Account \\\n --record-id 001XXXXXXXXXXXX \\\n --values \"Industry='Healthcare'\" \\\n --target-org myorg\n```\n\n### Delete Record\n```bash\nsf data delete record \\\n --sobject Account \\\n --record-id 001XXXXXXXXXXXX \\\n --target-org myorg\n```\n\n### Get Record\n```bash\nsf data get record \\\n --sobject Account \\\n --record-id 001XXXXXXXXXXXX \\\n --target-org myorg\n```\n\n## Bulk Operations\n\n### Bulk Import\n```bash\nsf data import bulk \\\n --file data.csv \\\n --sobject Account \\\n --target-org myorg \\\n --wait 30\n```\n\n### Bulk Update\n```bash\nsf data update bulk \\\n --file updates.csv \\\n --sobject Account \\\n --target-org myorg \\\n --wait 30\n```\n\n### Bulk Upsert\n```bash\nsf data upsert bulk \\\n --file data.csv \\\n --sobject Account \\\n --external-id External_Id__c \\\n --target-org myorg \\\n --wait 30\n```\n\n### Bulk Delete\n```bash\nsf data delete bulk \\\n --file ids.csv \\\n --sobject Account \\\n --target-org myorg \\\n --wait 30\n```\n\n## Tree Operations\n\n### Export Tree\n```bash\nsf data export tree \\\n --query \"SELECT Id, Name, (SELECT Id, Name FROM Contacts) FROM Account\" \\\n --output-dir ./data \\\n --target-org myorg\n```\n\n### Import Tree\n```bash\nsf data import tree \\\n --files data.json \\\n --target-org myorg\n```\n\n## Common Flags\n\n| Flag | Description |\n|------|-------------|\n| `--target-org`, `-o` | Target org alias |\n| `--json` | JSON output format |\n| `--result-format` | human, csv, json |\n| `--wait` | Minutes to wait for bulk jobs |\n| `--use-tooling-api` | Query Tooling API |\n\n## Apex Execution\n\n```bash\nsf apex run --file script.apex --target-org myorg\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":2321,"content_sha256":"591fac727e65cdeb611c318a614ce7f78603abe10122eea458f1eb78779061c8"},{"filename":"references/soql-relationship-guide.md","content":"\u003c!-- Parent: sf-data/SKILL.md -->\n# SOQL Relationship Query Guide\n\nComplete reference for querying related records in Salesforce.\n\n## Relationship Types\n\n### 1. Parent-to-Child (Subquery)\nReturns parent records with nested child records.\n\n```sql\nSELECT Id, Name,\n (SELECT Id, FirstName, LastName FROM Contacts)\nFROM Account\nWHERE Industry = 'Technology'\n```\n\n**Key Points:**\n- Maximum 20 subqueries per query\n- Use relationship name (plural): `Contacts`, `Opportunities`, `Cases`\n- For custom objects: `Child_Object__r`\n\n### 2. Child-to-Parent (Dot Notation)\nAccess parent fields from child record.\n\n```sql\nSELECT Id, Name,\n Account.Name,\n Account.Industry,\n Account.Owner.Name\nFROM Contact\nWHERE Account.Type = 'Customer'\n```\n\n**Key Points:**\n- Maximum 5 levels deep\n- Standard: `Account.Name`\n- Custom: `Parent__r.Name`\n\n### 3. Polymorphic (TYPEOF)\nHandle fields that reference multiple object types.\n\n```sql\nSELECT Id, Subject,\n TYPEOF Who\n WHEN Contact THEN FirstName, LastName\n WHEN Lead THEN Company\n END,\n TYPEOF What\n WHEN Account THEN Name, Industry\n WHEN Opportunity THEN Amount\n END\nFROM Task\n```\n\n**Common Polymorphic Fields:**\n- `WhoId` → Contact, Lead\n- `WhatId` → Account, Opportunity, Case, etc.\n- `OwnerId` → User, Queue\n\n## Relationship Names\n\n| Child Object | Parent Field | Relationship Name |\n|--------------|--------------|-------------------|\n| Contact | AccountId | Account.Contacts |\n| Opportunity | AccountId | Account.Opportunities |\n| Case | AccountId | Account.Cases |\n| Task | WhatId | Account.Tasks |\n| Contact | ReportsToId | Contact.ReportsTo |\n\n## Limits\n\n| Limit | Value |\n|-------|-------|\n| Child-to-Parent depth | 5 levels |\n| Subqueries per query | 20 |\n| Rows per subquery | 200 (without LIMIT) |\n\n## Best Practices\n\n1. **Use indexed fields** in WHERE clauses\n2. **Add LIMIT** to subqueries\n3. **Filter early** - push conditions into subqueries\n4. **Avoid N+1** - use relationship queries instead of loops\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":2004,"content_sha256":"a134d99f6b8dc34ed47b27b237292bb5a6ecb7d86d38eca4fa6b56cf30704b50"},{"filename":"references/test-data-best-practices.md","content":"\u003c!-- Parent: sf-data/SKILL.md -->\n# Test Data Best Practices\n\nUse this guide when creating or troubleshooting org data for demos, automation validation, integration checks, or post-deploy smoke testing.\n\n## Describe-first rule\n\nBefore creating or updating records, validate the object shape with:\n\n```bash\nsf sobject describe --sobject ObjectName --target-org \u003calias> --json\n```\n\nUse describe output to confirm:\n- required fields\n- createable and updateable fields\n- valid picklist values\n- parent relationships\n- fields that should be excluded from the payload\n\n### Handy filters\n\n```bash\n# Required + createable fields\njq '.result.fields[] | select(.nillable==false and .createable==true) | {name, type}'\n\n# Picklist values for one field\njq '.result.fields[] | select(.name==\"Status__c\") | .picklistValues[].value'\n\n# Fields that are not createable\njq '.result.fields[] | select(.createable==false) | .name'\n```\n\n## CLI-first for straightforward CRUD\n\nPrefer `sf data` commands when the workflow is simple and direct.\n\n```bash\nsf data create record \\\n --sobject Account \\\n --values \"Name='Bulk Test 001' Industry='Technology'\" \\\n --target-org \u003calias> \\\n --json\n```\n\nUse anonymous Apex when you need:\n- multi-object orchestration in one transaction\n- reusable factory logic\n- rollback via savepoint\n- complex branching that would be awkward in shell commands\n\n## Pre-flight checklist\n\nBefore data creation:\n- [ ] target object exists in the org\n- [ ] required parent records exist\n- [ ] required fields are known\n- [ ] picklist values are validated from describe output\n- [ ] non-createable fields are excluded\n- [ ] cleanup plan is ready\n\n## Common failure patterns\n\n| Failure | Likely cause | Fix |\n|---|---|---|\n| `REQUIRED_FIELD_MISSING` | Required field omitted | add describe-backed required values |\n| invalid picklist value | guessed value | inspect real picklist values first |\n| non-writeable field error | field is not createable/updateable | remove it from the payload |\n| `INVALID_CROSS_REFERENCE_KEY` | missing or wrong parent Id | create/query parent first |\n| `FIELD_CUSTOM_VALIDATION_EXCEPTION` | validation rule blocked test data | use a valid scenario or adjust setup |\n\n## Bounded retry strategy\n\nUse a small, explicit retry loop:\n\n1. **Attempt 1** — run the primary CLI command\n2. **Attempt 2** — retry once with corrected parameters\n3. **Attempt 3** — re-run describe, verify assumptions, and pivot if needed\n4. **Stop** — provide a manual workaround or alternate loading strategy\n\nDo not keep repeating the same failing shape.\n\n## Manual fallback options\n\nIf direct record creation keeps failing:\n- switch to tree import for parent-child data\n- switch to anonymous Apex for transactional setup\n- split the operation into parent-first then child records\n- provide the user with an exact manual setup checklist\n\n## Cleanup expectations\n\nWhenever test data is created, provide one of:\n- delete-by-ID commands\n- delete-by-pattern guidance\n- delete-by-created-date guidance\n- rollback script or savepoint flow\n\n## Related references\n\n- [sf-cli-data-commands.md](sf-cli-data-commands.md)\n- [test-data-patterns.md](test-data-patterns.md)\n- [cleanup-rollback-guide.md](cleanup-rollback-guide.md)\n- [orchestration.md](orchestration.md)\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":3263,"content_sha256":"1071c94a8ce945081920d3501b3b469b2f13e80cf3db9f8add008455bf54a49f"},{"filename":"references/test-data-factory-usage.md","content":"\u003c!-- Parent: sf-data/SKILL.md -->\n# Test Data Factory Usage\n\nHow to use and customize the test data factory templates.\n\n## Basic Factory Pattern\n\n### Account Factory\n```apex\npublic class TestDataFactory_Account {\n\n // Basic creation - inserted\n public static List\u003cAccount> create(Integer count) {\n return create(count, true);\n }\n\n // Control insertion\n public static List\u003cAccount> create(Integer count, Boolean doInsert) {\n List\u003cAccount> records = new List\u003cAccount>();\n for (Integer i = 0; i \u003c count; i++) {\n records.add(new Account(\n Name = 'Test Account ' + i,\n Industry = 'Technology'\n ));\n }\n if (doInsert) {\n insert records;\n }\n return records;\n }\n}\n```\n\n### Usage Examples\n\n```apex\n// Create 10 accounts (inserted)\nList\u003cAccount> accounts = TestDataFactory_Account.create(10);\n\n// Create 10 accounts (not inserted - for customization)\nList\u003cAccount> accounts = TestDataFactory_Account.create(10, false);\naccounts[0].AnnualRevenue = 1000000;\naccounts[1].Rating = 'Hot';\ninsert accounts;\n```\n\n## Factory with Variations\n\n### Industry Variations\n```apex\npublic static List\u003cAccount> createWithVariedIndustries(Integer count) {\n List\u003cAccount> records = new List\u003cAccount>();\n List\u003cString> industries = new List\u003cString>{\n 'Technology', 'Healthcare', 'Finance', 'Manufacturing', 'Retail'\n };\n\n for (Integer i = 0; i \u003c count; i++) {\n records.add(new Account(\n Name = 'Test Account ' + i,\n Industry = industries[Math.mod(i, industries.size())]\n ));\n }\n insert records;\n return records;\n}\n```\n\n### Revenue Tiers\n```apex\npublic static List\u003cAccount> createWithRevenueTiers(Integer count) {\n List\u003cAccount> records = new List\u003cAccount>();\n List\u003cDecimal> tiers = new List\u003cDecimal>{\n 100000, 500000, 1000000, 5000000, 10000000\n };\n\n for (Integer i = 0; i \u003c count; i++) {\n records.add(new Account(\n Name = 'Test Account ' + i,\n AnnualRevenue = tiers[Math.mod(i, tiers.size())]\n ));\n }\n insert records;\n return records;\n}\n```\n\n## Factory with Relationships\n\n### Contact Factory with Account\n```apex\npublic class TestDataFactory_Contact {\n\n // Create contacts for existing accounts\n public static List\u003cContact> createForAccounts(Set\u003cId> accountIds, Integer perAccount) {\n List\u003cContact> contacts = new List\u003cContact>();\n Integer i = 0;\n\n for (Id accId : accountIds) {\n for (Integer j = 0; j \u003c perAccount; j++) {\n contacts.add(new Contact(\n FirstName = 'Test',\n LastName = 'Contact ' + i,\n AccountId = accId,\n Email = 'test' + i + '@example.com'\n ));\n i++;\n }\n }\n insert contacts;\n return contacts;\n }\n}\n```\n\n### Usage\n```apex\n// Create 10 accounts with 3 contacts each\nList\u003cAccount> accounts = TestDataFactory_Account.create(10);\nSet\u003cId> accountIds = new Map\u003cId, Account>(accounts).keySet();\nList\u003cContact> contacts = TestDataFactory_Contact.createForAccounts(accountIds, 3);\n// Total: 10 accounts + 30 contacts\n```\n\n## Hierarchy Factory\n\n### Complete Hierarchy in One Call\n```apex\npublic class TestDataFactory_Hierarchy {\n\n public class HierarchyResult {\n public List\u003cAccount> accounts;\n public List\u003cContact> contacts;\n public List\u003cOpportunity> opportunities;\n }\n\n public static HierarchyResult create(\n Integer accountCount,\n Integer contactsPerAccount,\n Integer oppsPerAccount\n ) {\n HierarchyResult result = new HierarchyResult();\n\n // Create accounts\n result.accounts = TestDataFactory_Account.create(accountCount);\n Set\u003cId> accountIds = new Map\u003cId, Account>(result.accounts).keySet();\n\n // Create contacts\n result.contacts = TestDataFactory_Contact.createForAccounts(\n accountIds, contactsPerAccount\n );\n\n // Create opportunities\n result.opportunities = TestDataFactory_Opportunity.createForAccounts(\n accountIds, oppsPerAccount\n );\n\n return result;\n }\n}\n```\n\n### Usage\n```apex\n// Create complete test hierarchy\nHierarchyResult data = TestDataFactory_Hierarchy.create(\n 10, // accounts\n 3, // contacts per account\n 2 // opportunities per account\n);\n// Total: 10 accounts + 30 contacts + 20 opportunities\n\nSystem.debug('Accounts: ' + data.accounts.size());\nSystem.debug('Contacts: ' + data.contacts.size());\nSystem.debug('Opportunities: ' + data.opportunities.size());\n```\n\n## Edge Case Factories\n\n### Boundary Values\n```apex\npublic static Account createWithMinValues() {\n return new Account(\n Name = 'A' // Minimum name length\n );\n}\n\npublic static Account createWithMaxValues() {\n return new Account(\n Name = 'X'.repeat(255), // Maximum name length\n Description = 'Y'.repeat(32000) // Maximum description\n );\n}\n```\n\n### Special Characters\n```apex\npublic static Account createWithSpecialCharacters() {\n return new Account(\n Name = 'Test & \"Special\" \u003cCharacters> \\'Quotes\\''\n );\n}\n```\n\n### Null/Empty Values\n```apex\npublic static List\u003cAccount> createWithNullableFields(Integer count) {\n List\u003cAccount> records = new List\u003cAccount>();\n for (Integer i = 0; i \u003c count; i++) {\n Account acc = new Account(Name = 'Test ' + i);\n // Leave optional fields null\n // acc.Industry = null;\n // acc.AnnualRevenue = null;\n records.add(acc);\n }\n insert records;\n return records;\n}\n```\n\n## Using Factories for Testing\n\n### Trigger Test Example\n```apex\n@isTest\npublic class AccountTriggerTest {\n\n @isTest\n static void testBulkInsert() {\n // Setup - 201 records to test batch boundaries\n Test.startTest();\n List\u003cAccount> accounts = TestDataFactory_Account.create(201);\n Test.stopTest();\n\n // Verify trigger logic executed\n List\u003cAccount> results = [\n SELECT Id, Custom_Field__c\n FROM Account\n WHERE Id IN :accounts\n ];\n\n for (Account acc : results) {\n System.assertNotEquals(null, acc.Custom_Field__c,\n 'Trigger should have set Custom_Field__c');\n }\n }\n\n @isTest\n static void testWithRelatedRecords() {\n // Setup hierarchy\n HierarchyResult data = TestDataFactory_Hierarchy.create(5, 2, 1);\n\n Test.startTest();\n // Test logic that involves relationships\n MyClass.processAccountsWithContacts(data.accounts);\n Test.stopTest();\n\n // Verify results\n }\n}\n```\n\n### Flow Test Example\n```apex\n@isTest\nstatic void testRecordTriggeredFlow() {\n // Create data that matches flow entry criteria\n List\u003cAccount> accounts = TestDataFactory_Account.create(10, false);\n for (Account acc : accounts) {\n acc.Industry = 'Technology'; // Matches flow filter\n acc.AnnualRevenue = 1000000; // Triggers flow action\n }\n\n Test.startTest();\n insert accounts; // Flow fires on insert\n Test.stopTest();\n\n // Verify flow results\n List\u003cTask> tasks = [SELECT Id FROM Task WHERE WhatId IN :accounts];\n System.assertEquals(10, tasks.size(), 'Flow should create task for each account');\n}\n```\n\n## Best Practices\n\n1. **Always return inserted records** - Caller may need IDs\n2. **Provide doInsert parameter** - Allow customization before insert\n3. **Use bulk patterns** - Single DML for multiple records\n4. **Handle required fields** - Prevent validation errors\n5. **Use unique values** - Avoid duplicate rule violations\n6. **Track IDs for cleanup** - Enable proper test isolation\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":7761,"content_sha256":"f9e8d6387e5124b97887c6344438c290d66466f4ecd2a73fced6419d0e93e601"},{"filename":"references/test-data-patterns.md","content":"\u003c!-- Parent: sf-data/SKILL.md -->\n# Test Data Patterns Guide\n\nBest practices for creating realistic and effective test data.\n\n## Factory Pattern\n\n### Standard Implementation\n\n```apex\npublic class TestDataFactory_Account {\n\n public static List\u003cAccount> create(Integer count) {\n return create(count, true);\n }\n\n public static List\u003cAccount> create(Integer count, Boolean doInsert) {\n List\u003cAccount> records = new List\u003cAccount>();\n for (Integer i = 0; i \u003c count; i++) {\n records.add(buildRecord(i));\n }\n if (doInsert) {\n insert records;\n }\n return records;\n }\n\n private static Account buildRecord(Integer index) {\n return new Account(\n Name = 'Test Account ' + index,\n Industry = 'Technology'\n );\n }\n}\n```\n\n### Key Principles\n\n1. **Always create in lists** - Support bulk operations\n2. **Provide doInsert parameter** - Caller controls insertion\n3. **Track IDs for cleanup** - Return inserted records\n4. **Use realistic data** - Valid picklist values\n\n## Record Count Recommendations\n\n| Test Scenario | Record Count | Why |\n|---------------|--------------|-----|\n| Basic unit test | 1-10 | Quick validation |\n| Trigger testing | 201 | Batch boundary |\n| Flow testing | 200 | Single transaction |\n| Batch Apex | 500+ | Multiple batches |\n| Performance | 1000+ | Stress testing |\n\n## Edge Cases to Test\n\n### Null Values\n```apex\naccount.Industry = null; // Test null handling\n```\n\n### Boundary Values\n```apex\naccount.Name = 'A'; // Min length\naccount.Name = String.valueOf('X').repeat(255); // Max length\n```\n\n### Special Characters\n```apex\naccount.Name = 'Test & \"Special\" \u003cCharacters>';\n```\n\n### Date Boundaries\n```apex\nopp.CloseDate = Date.today(); // Today\nopp.CloseDate = Date.today().addDays(-1); // Yesterday\nopp.CloseDate = Date.newInstance(2000, 1, 1); // Old date\n```\n\n## Relationship Testing\n\n### Create Hierarchy\n```apex\n// Create parent first\nList\u003cAccount> accounts = TestDataFactory_Account.create(10);\n\n// Create children with parent references\nList\u003cContact> contacts = TestDataFactory_Contact.createForAccounts(\n new List\u003cId>(new Map\u003cId, Account>(accounts).keySet()),\n 5 // 5 contacts per account\n);\n```\n\n## Best Practices\n\n1. **Avoid hardcoded IDs** - Use dynamic references\n2. **Create all required fields** - Prevent validation errors\n3. **Use unique values** - Prevent duplicate errors\n4. **Document patterns** - Explain test scenarios\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":2485,"content_sha256":"a5bd7ad4cd3addec6675e5ca02a4dd35a9d1721e9be4c6ff615908eb46303b4d"}],"content_json":{"type":"doc","content":[{"type":"heading","attrs":{"level":1},"content":[{"text":"Salesforce Data Operations Expert (sf-data)","type":"text"}]},{"type":"paragraph","content":[{"text":"Use this skill when the user needs ","type":"text"},{"text":"Salesforce data work","type":"text","marks":[{"type":"strong"}]},{"text":": record CRUD, bulk import/export, test data generation, cleanup scripts, or data factory patterns for validating Apex, Flow, or integration behavior.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"When This Skill Owns the Task","type":"text"}]},{"type":"paragraph","content":[{"text":"Use ","type":"text"},{"text":"sf-data","type":"text","marks":[{"type":"code_inline"}]},{"text":" when the work involves:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"sf data","type":"text","marks":[{"type":"code_inline"}]},{"text":" CLI commands","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"record creation, update, delete, upsert, export, or tree import/export","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"realistic test data generation","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"bulk data operations and cleanup","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Apex anonymous scripts for data seeding / rollback","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Delegate elsewhere when the user is:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"writing SOQL only → ","type":"text"},{"text":"sf-soql","type":"text","marks":[{"type":"link","attrs":{"href":"../sf-soql/SKILL.md","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"running or repairing Apex tests → ","type":"text"},{"text":"sf-testing","type":"text","marks":[{"type":"link","attrs":{"href":"../sf-testing/SKILL.md","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"deploying metadata first → ","type":"text"},{"text":"sf-deploy","type":"text","marks":[{"type":"link","attrs":{"href":"../sf-deploy/SKILL.md","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"discovering schema / field definitions → ","type":"text"},{"text":"sf-metadata","type":"text","marks":[{"type":"link","attrs":{"href":"../sf-metadata/SKILL.md","title":null}}]}]}]}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Important Mode Decision","type":"text"}]},{"type":"paragraph","content":[{"text":"Confirm which mode the user wants:","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Mode","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Use when","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Script generation","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"they want reusable ","type":"text"},{"text":".apex","type":"text","marks":[{"type":"code_inline"}]},{"text":", CSV, or JSON assets without touching an org yet","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Remote execution","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"they want records created / changed in a real org now","type":"text"}]}]}]}]},{"type":"paragraph","content":[{"text":"Do not assume remote execution if the user may only want scripts.","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Required Context to Gather First","type":"text"}]},{"type":"paragraph","content":[{"text":"Ask for or infer:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"target object(s)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"org alias, if remote execution is required","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"operation type: query, create, update, delete, upsert, import, export, cleanup","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"expected volume","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"whether this is test data, migration data, or one-off troubleshooting data","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"any parent-child relationships that must exist first","type":"text"}]}]}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Core Operating Rules","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"sf-data","type":"text","marks":[{"type":"code_inline"}]},{"text":" acts on ","type":"text"},{"text":"remote org data","type":"text","marks":[{"type":"strong"}]},{"text":" unless the user explicitly wants local script generation.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Objects and fields must already exist before data creation.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"For automation testing, prefer ","type":"text"},{"text":"251+ records","type":"text","marks":[{"type":"strong"}]},{"text":" when bulk behavior matters.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Always think about cleanup before creating large or noisy datasets.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Never use real PII in generated test data.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Prefer ","type":"text"},{"text":"CLI-first","type":"text","marks":[{"type":"strong"}]},{"text":" for straightforward CRUD; use anonymous Apex when the operation truly needs server-side orchestration.","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"If metadata is missing, stop and hand off to:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"sf-metadata","type":"text","marks":[{"type":"link","attrs":{"href":"../sf-metadata/SKILL.md","title":null}}]},{"text":" or ","type":"text"},{"text":"sf-deploy","type":"text","marks":[{"type":"link","attrs":{"href":"../sf-deploy/SKILL.md","title":null}}]}]}]}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Recommended Workflow","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"1. Verify prerequisites","type":"text"}]},{"type":"paragraph","content":[{"text":"Confirm object / field availability, org auth, and required parent records.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"2. Run describe-first pre-flight validation when schema is uncertain","type":"text"}]},{"type":"paragraph","content":[{"text":"Before creating or updating records, use object describe data to validate:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"required fields","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"createable vs non-createable fields","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"picklist values","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"relationship fields and parent requirements","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Example pattern:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"sf sobject describe --sobject ObjectName --target-org \u003calias> --json","type":"text"}]},{"type":"paragraph","content":[{"text":"Helpful filters:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Required + createable fields\njq '.result.fields[] | select(.nillable==false and .createable==true) | {name, type}'\n\n# Valid picklist values for one field\njq '.result.fields[] | select(.name==\"StageName\") | .picklistValues[].value'\n\n# Fields that cannot be set on create\njq '.result.fields[] | select(.createable==false) | .name'","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"3. Choose the smallest correct mechanism","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Need","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Default approach","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"small one-off CRUD","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"sf data","type":"text","marks":[{"type":"code_inline"}]},{"text":" single-record commands","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"large import/export","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Bulk API 2.0 via ","type":"text"},{"text":"sf data ... bulk","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"parent-child seed set","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"tree import/export","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"reusable test dataset","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"factory / anonymous Apex script","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"reversible experiment","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"cleanup script or savepoint-based approach","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"4. Execute or generate assets","type":"text"}]},{"type":"paragraph","content":[{"text":"Use the built-in templates under ","type":"text"},{"text":"assets/","type":"text","marks":[{"type":"code_inline"}]},{"text":" when they fit:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"assets/factories/","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"assets/bulk/","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"assets/cleanup/","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"assets/soql/","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"assets/csv/","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"assets/json/","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"5. Verify results","type":"text"}]},{"type":"paragraph","content":[{"text":"Check counts, relationships, and record IDs after creation or update.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"6. Apply a bounded retry strategy","type":"text"}]},{"type":"paragraph","content":[{"text":"If creation fails:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"try the primary CLI shape once","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"retry once with corrected parameters","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"re-run describe / validate assumptions","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"pivot to a different mechanism or provide a manual workaround","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Do ","type":"text"},{"text":"not","type":"text","marks":[{"type":"strong"}]},{"text":" repeat the same failing command indefinitely.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"7. Leave cleanup guidance","type":"text"}]},{"type":"paragraph","content":[{"text":"Provide exact cleanup commands or rollback assets whenever data was created.","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"High-Signal Rules","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Bulk safety","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"use bulk operations for large volumes","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"test automation-sensitive behavior with 251+ records where appropriate","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"avoid one-record-at-a-time patterns for bulk scenarios","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Data integrity","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"include required fields","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"validate picklist values before creation","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"verify parent IDs and relationship integrity","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"account for validation rules and duplicate constraints","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"exclude non-createable fields from input payloads","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Cleanup discipline","type":"text"}]},{"type":"paragraph","content":[{"text":"Prefer one of:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"delete-by-ID","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"delete-by-pattern","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"delete-by-created-date window","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"rollback / savepoint patterns for script-based test runs","type":"text"}]}]}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Common Failure Patterns","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Error","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Likely cause","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Default fix direction","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"INVALID_FIELD","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"wrong field API name or FLS issue","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"verify schema and access","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"REQUIRED_FIELD_MISSING","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"mandatory field omitted","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"include required values from describe data","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"INVALID_CROSS_REFERENCE_KEY","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"bad parent ID","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"create / verify parent first","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"FIELD_CUSTOM_VALIDATION_EXCEPTION","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"validation rule blocked the record","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"use valid test data or adjust setup","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"invalid picklist value","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"guessed value instead of describe-backed value","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"inspect picklist values first","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"non-writeable field error","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"field is not createable / updateable","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"remove it from the payload","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"bulk limits / timeouts","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"wrong tool for the volume","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"switch to bulk / staged import","type":"text"}]}]}]}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Output Format","type":"text"}]},{"type":"paragraph","content":[{"text":"When finishing, report in this order:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Operation performed","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Objects and counts","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Target org or local artifact path","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Record IDs / output files","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Verification result","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Cleanup instructions","type":"text","marks":[{"type":"strong"}]}]}]}]},{"type":"paragraph","content":[{"text":"Suggested shape:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"text"},"content":[{"text":"Data operation: \u003ccreate / update / delete / export / seed>\nObjects: \u003cobject + counts>\nTarget: \u003corg alias or local path>\nArtifacts: \u003crecord ids / csv / apex / json files>\nVerification: \u003cpassed / partial / failed>\nCleanup: \u003cexact delete or rollback guidance>","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Cross-Skill Integration","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Need","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Delegate to","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Reason","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"discover object / field structure","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"sf-metadata","type":"text","marks":[{"type":"link","attrs":{"href":"../sf-metadata/SKILL.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"accurate schema grounding","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"run bulk-sensitive Apex validation","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"sf-testing","type":"text","marks":[{"type":"link","attrs":{"href":"../sf-testing/SKILL.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"test execution and coverage","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"deploy missing schema first","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"sf-deploy","type":"text","marks":[{"type":"link","attrs":{"href":"../sf-deploy/SKILL.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"metadata readiness","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"implement production logic consuming the data","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"sf-apex","type":"text","marks":[{"type":"link","attrs":{"href":"../sf-apex/SKILL.md","title":null}}]},{"text":" or ","type":"text"},{"text":"sf-flow","type":"text","marks":[{"type":"link","attrs":{"href":"../sf-flow/SKILL.md","title":null}}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"behavior implementation","type":"text"}]}]}]}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Reference Map","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Start here","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/sf-cli-data-commands.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/sf-cli-data-commands.md","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/test-data-best-practices.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/test-data-best-practices.md","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/orchestration.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/orchestration.md","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/test-data-patterns.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/test-data-patterns.md","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/test-data-factory-usage.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/test-data-factory-usage.md","title":null}}]}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Query / bulk / cleanup","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/soql-relationship-guide.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/soql-relationship-guide.md","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/relationship-query-examples.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/relationship-query-examples.md","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/bulk-operations-guide.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/bulk-operations-guide.md","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/cleanup-rollback-guide.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/cleanup-rollback-guide.md","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/cleanup-rollback-example.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/cleanup-rollback-example.md","title":null}}]}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Examples / limits","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/crud-workflow-example.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/crud-workflow-example.md","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/bulk-testing-example.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/bulk-testing-example.md","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/anonymous-apex-guide.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/anonymous-apex-guide.md","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/governor-limits-reference.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/governor-limits-reference.md","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"assets/","type":"text","marks":[{"type":"link","attrs":{"href":"assets/","title":null}}]}]}]}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Score Guide","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Score","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Meaning","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"117+","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"strong production-safe data workflow","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"104–116","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"good operation with minor improvements possible","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"91–103","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"acceptable but review advised","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"78–90","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"partial / risky patterns present","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\u003c 78","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"blocked until corrected","type":"text"}]}]}]}]}]},"metadata":{"date":"2026-11-02","name":"sf-data","author":"@skillopedia","source":{"stars":423,"repo_name":"sf-skills","origin_url":"https://github.com/Jaganpro/sf-skills/blob/HEAD/skills/sf-data/SKILL.md","repo_owner":"Jaganpro","body_sha256":"d4b3ed9c12059321145576aa9f469c310aaba2aa9ce882804895f0727b614c38","cluster_key":"e65ecdc48dac7dddc1b1e0aad364f9dc3d611c8466ce352c33b4d197e8617268","clean_bundle":{"format":"clean-skill-bundle-v1","source":"Jaganpro/sf-skills/skills/sf-data/SKILL.md","text_bytes":107852,"source_commit":"53c9956c4279f8e9abda51164a3e9444811f0e8e","skills_sh_rank":10402,"attachment_count":25,"text_attachments":25,"attachment_storage":"skill_attachments","binary_attachments":0},"cluster_size":1,"skill_md_path":"skills/sf-data/SKILL.md","import_metadata":{"date":"2026-11-02","author":"@skillopedia","version":"v1","category":"integrations-apis","category_label":"Integrations"},"exact_dupes_collapsed_into_this":0},"license":"MIT","version":"v1","category":"integrations-apis","metadata":{"author":"Jag Valaiyapathy","scoring":"130 points across 7 categories","version":"1.2.0"},"import_tag":"skills-sh-top20k-2026-11","description":"Salesforce data operations with 130-point scoring. TRIGGER when: user creates test data, performs bulk import/export, uses sf data CLI commands, or needs data factory patterns for Apex tests. DO NOT TRIGGER when: SOQL query writing only (use sf-soql), Apex test execution (use sf-testing), or metadata deployment (use sf-deploy).\n"}},"renderedAt":1793665985802}

Salesforce Data Operations Expert (sf-data) Use this skill when the user needs Salesforce data work : record CRUD, bulk import/export, test data generation, cleanup scripts, or data factory patterns for validating Apex, Flow, or integration behavior. When This Skill Owns the Task Use when the work involves: - CLI commands - record creation, update, delete, upsert, export, or tree import/export - realistic test data generation - bulk data operations and cleanup - Apex anonymous scripts for data seeding / rollback Delegate elsewhere when the user is: - writing SOQL only → sf-soql - running or r…