forcedotcom/sf-skills

platform-custom-report-type-generate

Use this skill when users need to create, generate, or validate Salesforce Custom Report Type metadata.

View source
Original skill document

Rendered from the source repository. Headings, examples, code, tables, links, and referenced images are preserved.

Specification

Salesforce Custom Report Type Metadata Knowledge

Overview

Custom Report Types (CRTs) define the data framework for Salesforce reports. They specify a primary object, up to 3 related objects, the relationship (join) between them, and which fields are available in the report builder.

Purpose

  • Enable reporting across custom objects and custom relationships not covered by standard report types
  • Curate a focused set of fields for report builders (including fields reached via lookup)
  • Control inner/outer join behavior to include or exclude primary records without related records

Configuration

File extension: .reportType-meta.xml. The file basename is the report type's developer name (e.g. AccountsWithProjects.reportType-meta.xml). Each CRT is a single file, not nested under an object folder.

Key Elements

Top-level <ReportType> children:

ElementRequiredNotes
<fullName>YesAPI identifier; must match the file name. Letters, numbers, underscores; must begin with a letter; no spaces; no trailing underscore; no consecutive underscores
<label>YesHuman-friendly name shown in the report type picker
<description>RecommendedState the business "why" — who uses this and what they learn
<baseObject>YesAPI name of the primary object (e.g. Account, Project__c). Cannot be changed after initial creation. All objects, including custom and external, are supported (external objects from API 38.0+)
<category>RecommendedReport builder category — see references/category-values.md
<deployed>Yestrue to expose to users; false while building/iterating
<join>ConditionalAdds a related object and its join behavior. Nest further <join> blocks for deeper relationships
<sections>RecommendedGroups of columns available to the report type. Though not strictly required, a report without columns isn't useful

<sections> (group of columns) sub-elements:

ElementRequiredNotes
<masterLabel>YesSection heading shown in the report builder
<columns>ConditionalOne per field exposed in the section

<columns> (single field) sub-elements:

ElementRequiredNotes
<field>YesField API name (or dotted lookup-traversal path)
<table>YesThe object the field belongs to — base object name or dotted relationship path
<checkedByDefault>Yestrue if the column is selected by default in the report builder
<displayNameOverride>NoCustom column label shown in the report builder, overriding the field's default label

Critical Rules (Read First)

Rule 1: If <fullName> Is Present, It Must Match the File Name

In source format, fullName is inherited from Metadata and derived from the file name, so the <fullName> element is technically optional. The repo convention is to include it. If you include `<fullName>`, its value must equal the file name (everything before `.reportType-meta.xml`) exactly — same characters, same casing, same underscores.

Wrong — file name and <fullName> differ:

  • File: account_projects.reportType-meta.xml
  • <fullName>AccountProjects</fullName>

(Mismatch: file uses account_projects, fullName uses AccountProjects)

Right — file name and <fullName> are identical:

  • File: AccountProjects.reportType-meta.xml
  • <fullName>AccountProjects</fullName>

Rule 2: Join Semantics — outerJoin Controls Inclusion

Each <join> block has an <outerJoin> element that determines which primary records appear in the report:

<outerJoin> valueBehaviorReport Builder Label
falseInner join — only primary records that HAVE at least one related record"Each 'A' record must have at least one related 'B' record"
trueOuter join — all primary records, with or without related records"'A' records may or may not have related 'B' records"

Default when unspecified: Use true (outer join) when the user wants to see all primary records regardless of children. Use false when the report only makes sense if children exist.

Rule 3: Each Object Needs Its Own <sections> Block

Every object in the CRT (primary + each joined object) must have a corresponding <sections> block that lists the fields exposed for reporting. Without a section for an object, none of its fields appear in the report builder.

  • <masterLabel> on each section is the section heading in the report builder
  • <columns> entries list the fields — each with a <field> (API name) and <table> (object API name)
  • For fields reached via lookup, use the relationship path in <field> (e.g. Owner.Name with <table> set to the owning object)

Rule 4: Field API Names, Not Labels

Use exact API names for fields: standard fields use their defined names (Name, CreatedDate, OwnerId), custom fields use Field__c. Custom objects must include __c.

Wrong:

  • <field>Account Name</field>

Right:

  • <field>Name</field> with <table>Account</table>

Rule 5: Relationship Path for Joined Objects

When adding a <join>, the <relationship> element must use the child relationship name as defined on the lookup/master-detail field pointing from the child object to the parent. For custom relationships, this typically ends in __r.

Wrong:

  • <relationship>Project</relationship> (for a custom child relationship)

Right:

  • <relationship>Projects__r</relationship> (child relationship name)
  • <relationship>Contacts</relationship> (standard, non-custom child relationship)

Rule 6: Maximum 4 Objects Total in a Join Chain

A single CRT can join a maximum of four objects total (the base object + up to 3 additional objects via nested <join> blocks).

Rule 7: No Inner Join After an Outer Join

Once the join chain contains an outer join (<outerJoin>true</outerJoin>), every subsequent nested join must also be an outer join. An inner join that follows an outer join earlier in the sequence is not allowed.

Wrong:

xml
<join>
    <outerJoin>true</outerJoin>        <!-- outer join first -->
    <relationship>Contacts</relationship>
    <join>
        <outerJoin>false</outerJoin>   <!-- WRONG: inner join after outer -->
        <relationship>Assets</relationship>
    </join>
</join>

Right:

xml
<join>
    <outerJoin>true</outerJoin>
    <relationship>Contacts</relationship>
    <join>
        <outerJoin>true</outerJoin>    <!-- outer stays outer -->
        <relationship>Assets</relationship>
    </join>
</join>

Rule 8: <table> for Joined Objects Uses Dotted Path

In <sections>, the <table> element identifies which object in the join chain each column belongs to. For the base object, use the object name directly (e.g. Account). For joined objects, use the dotted relationship path from the base object.

Object in chain<table> value
Base (Account)Account
First join (Account → Contacts)Account.Contacts
Nested join (Account → Contacts → Assets)Account.Contacts.Assets

Rule 9: Field Paths Can Traverse Lookups

<field> values may reference fields reached via lookup relationships using dot notation — for example Owner.Email (owner User's email) or ReportsTo.CreatedBy.Contact.Owner.MobilePhone. The <table> must still be the object that owns the starting field.

Rule 10: Historical Trending Fields Use _hst Suffix

For a field with trackTrending=true, the API name in <field> and <table> uses the _hst suffix:

xml
<columns>
    <checkedByDefault>false</checkedByDefault>
    <field>Field2__c_hst</field>
    <table>CustomTrendedObject__c.CustomTrendedObject__c_hst</table>
</columns>

Rule 11: Primary Object Cannot Be Changed After Deployment

Once deployed, the <baseObject> of a CRT is locked. To change the primary object, create a new CRT and retire the old one.

Rule 12: autogenerated Is Reserved for Historical Trending

The <autogenerated> element (API 29.0+) marks CRTs that Salesforce created automatically when historical trending was enabled on an object. Do not set this manually on hand-authored CRTs.

Generation Workflow

Step 1: Gather Requirements

  • Primary object API name (e.g. Account, Project__c)
  • Related objects and the relationship between each (which has the lookup/master-detail to which)
  • For each relationship: inner join (children required) or outer join (children optional)?
  • Which fields to expose per object — aim for task-relevant, not the full field list
  • Audience and category — where should this appear in the report builder picker?
  • Whether this ships as deployed=true now or stays deployed=false during iteration

Step 2: Examine Existing Examples

  • Look in the project for in-project CRT patterns
  • If existing report types have been retrieved from an org, compare against those structures

Step 3: Write the Specification

Document before authoring:

  • fullName and label
  • baseObject
  • Category and deployed state
  • Join chain: for each related object — relationship name, outer vs inner join
  • Section layout: one section per object, ordered list of fields
  • Acceptance criteria: which records should appear when the report runs, which fields are available in the builder

Step 4: Author the Metadata File

Start from the closest example in examples/ and adapt it to the user's scenario:

  • Primary object only (no joins) → examples/AccountsWithIndustry.reportType-meta.xml
  • Outer join (primary records included even without children) → examples/AccountsWithProjects.reportType-meta.xml
  • Nested inner join (every level requires children) → examples/AccountProjectsWithTasks.reportType-meta.xml

Name the file <DeveloperName>.reportType-meta.xml.

Step 5: Validate

  • Well-formed XML with correct namespace (xmlns="http://soap.sforce.com/2006/04/metadata")
  • File name (without .reportType-meta.xml) matches <fullName> when <fullName> is included
  • <baseObject> is a valid API name and the object is deployed
  • Every <relationship> uses the correct child relationship name (__r suffix for custom)
  • Each object referenced in <sections> is part of the CRT (primary or joined)
  • All <field> references exist on the parent <table> and use API names (not labels)
  • <category> is a valid Salesforce category value
  • <deployed> is true if users need to access the CRT immediately

Reference File Index

FileWhen to read
examples/AccountsWithIndustry.reportType-meta.xmlStep 2 / Step 4 — primary-object-only template
examples/AccountsWithProjects.reportType-meta.xmlStep 2 / Step 4 — outer-join template (primary included even without children)
examples/AccountProjectsWithTasks.reportType-meta.xmlStep 2 / Step 4 — nested inner-join template (every level requires children)
references/category-values.mdStep 3 — to choose a valid <category> value from the ReportTypeCategory enum
references/errors-and-troubleshooting.mdWhen fields don't appear in the report builder or join requirements conflict

Verification Checklist

Universal Checks

  • [ ] File extension is .reportType-meta.xml
  • [ ] File basename satisfies the developer-name rules (begins with a letter, only letters/numbers/underscores, no spaces, no trailing underscore, no consecutive underscores)
  • [ ] If <fullName> is included, it matches the file basename exactly (same characters, casing, and underscores)
  • [ ] <label> is human-readable and under 40 characters
  • [ ] <description> explains the business purpose
  • [ ] <baseObject> uses a valid API name and that object is deployed
  • [ ] <category> is a valid ReportTypeCategory enum value
  • [ ] <deployed> is set appropriately (true for user access, false for in-progress iteration)
  • [ ] <autogenerated> is NOT set manually (reserved for historical-trending CRTs)

Join Checks

  • [ ] Each <join> uses the correct child relationship name (not the lookup field API name)
  • [ ] Custom relationships use __r suffix
  • [ ] <outerJoin> is set intentionally: true = optional children, false = required children
  • [ ] No inner join (<outerJoin>false</outerJoin>) appears after an outer join earlier in the sequence
  • [ ] Total object count (base + joins, including nested) is 4 or fewer

Section Checks

  • [ ] Every object in the CRT has a corresponding <sections> block
  • [ ] <masterLabel> on each section is descriptive
  • [ ] Every <columns> has both <field> (API name) and <table> (object API name or dotted path)
  • [ ] <checkedByDefault> is set for each column
  • [ ] <table> for base object is the object API name (e.g. Account)
  • [ ] <table> for joined objects uses the dotted relationship path (e.g. Account.Projects__r, Account.Projects__r.Tasks__r)
  • [ ] Field references use API names (not labels); custom fields use __c
  • [ ] Lookup traversal fields use dot notation (e.g. Owner.Email) with <table> set to the object owning the starting field
  • [ ] Historical trending fields use _hst suffix in both <field> and <table> when applicable
  • [ ] No duplicate fields within a section
from this repository

More skills

All skills
forcedotcom
Community

agentforce-d360-analyze

Data Cloud 360° view of a single Agentforce session. TRIGGER when user asks to trace, inspect, summarize, or describe a specific Agentforce session by session id (Agent Session UUID 019d… or MessagingSession id 0Mw…). Also triggers on session discovery — find/list/search sessions by time, agent, channel, outcome, or conversation text — when the user has no session id yet. DO NOT TRIGGER for design-time architecture questions (use agentforce-architecture-analyze instead) or for runtime perf/latency/SLO questions that require platform telemetry beyond Data Cloud.

installs
1
GitHub stars
972
Updated
Sep 7
forcedotcom
Community

agentforce-generate

Build, modify, audit, repair, optimize, debug, and deploy agents with Agentforce Agent Script. TRIGGER when: user creates, reviews, or changes .agent files or aiAuthoringBundle metadata; asks to fix AgentScript, audit an existing agent, run an AgentScript health check, common-pitfall review, or baseline-versus-candidate repair loop; changes a response, action, subagent, route, state flow, or Agent Spec; previews, debugs, deploys, publishes, or tests agents; uses sf agent generate/preview/publish/test; or manages Agentforce MCP servers, tools, assets, or authentication. DO NOT TRIGGER when: Apex, Flow, Prompt Template, Experience Cloud, or general Salesforce CLI work is unrelated to Agent Script; or the primary input is a production session or trace ID rather than an agent artifact.

installs
1
GitHub stars
972
Updated
Sep 7
forcedotcom
Community

platform-quick-deploy

Deploy validated metadata to a Production Salesforce org without re-running tests. TRIGGER when the user wants to deploy to production, says 'quick deploy', 'promote', 'ship to prod', or has just validated and wants to push the change live. REQUIRES a recent sf project deploy validate job ID (≤10 days old, ≤3 days for --use-most-recent). DO NOT TRIGGER for sandbox/scratch deploys (use platform-metadata-deploy) or unvalidated deploys (use platform-deploy-validate first).

installs
1
GitHub stars
972
Updated
Sep 7
forcedotcom
Community

agentforce-test

Write, run, and analyze structured test suites for Agentforce agents — functional AND security. TRIGGER when: user writes or modifies test spec YAML (AiEvaluationDefinition); runs sf agent test create, run, run-eval, or results commands; asks about test coverage strategy, metric selection, or custom evaluations; interprets test results or diagnoses test failures; asks about batch testing, regression suites, or CI/CD test integration; requests security testing, OWASP LLM Top 10, red-teaming, penetration testing, prompt-injection tests, a security grade, or a vulnerability assessment of an agent. DO NOT TRIGGER when: user creates, modifies, previews, or debugs .agent files (use agentforce-generate); deploys or publishes agents; writes Agent Script code; uses sf agent preview for development iteration; analyzes production session traces (use agentforce-observe); performs a static safety review of .agent file content (use agentforce-generate Section 15).

installs
3
GitHub stars
972
Updated
Sep 7