diff --git a/skills/deploying-webapplication/SKILL.md b/skills/deploying-webapplication/SKILL.md
new file mode 100644
index 0000000..1f0a1e4
--- /dev/null
+++ b/skills/deploying-webapplication/SKILL.md
@@ -0,0 +1,77 @@
+---
+name: deploying-webapplication
+description: "Deploy a Salesforce web application to an org — the full deployment sequence including org authentication, pre-deploy build, metadata deployment, permission set assignment, data import, GraphQL schema fetch, and codegen. Use whenever the user wants to deploy, push to org, assign permission sets, import data, fetch GraphQL schema, run codegen, or set up an org after development. Triggers on: deploy, push to org, deploy metadata, assign permission set, import data, schema fetch, codegen, org auth, authenticate org, build and deploy, post-deploy, org setup."
+---
+
+# Deploying a Web Application
+
+The order of operations is critical when deploying to a Salesforce org. This sequence reflects the canonical flow.
+
+## Step 1: Org Authentication
+
+Check if the org is connected. If not, authenticate. All subsequent steps require an authenticated org.
+
+## Step 2: Pre-deploy Web Application Build
+
+Install dependencies and build the web application to produce `dist/`. Required before deploying web application entities.
+
+Run when: deploying web applications and `dist/` is missing or source has changed.
+
+## Step 3: Deploy Metadata
+
+Check for a manifest (`manifest/package.xml` or `package.xml`) first. If present, deploy using the manifest. If not, deploy all metadata from the project.
+
+Deploys objects, layouts, permission sets, Apex classes, web applications, and all other metadata. Must complete before schema fetch — the schema reflects org state.
+
+## Step 4: Post-deploy Configuration
+
+Deploying does not mean assigning. After deployment:
+
+- **Permission sets / groups** — assign to users so they have access to custom objects and fields. Required for GraphQL introspection to return the correct schema.
+- **Profiles** — ensure users have the correct profile.
+- **Other config** — named credentials, connected apps, custom settings, flow activation.
+
+Proactive behavior: after a successful deploy, discover permission sets in `force-app/main/default/permissionsets/` and assign each one (or ask the user).
+
+## Step 5: Data Import (optional)
+
+Only if `data/data-plan.json` exists. Delete runs in reverse plan order (children before parents). Import uses Anonymous Apex with duplicate rule save enabled.
+
+Always ask the user before importing or cleaning data.
+
+## Step 6: GraphQL Schema and Codegen
+
+1. Set default org
+2. Fetch schema (GraphQL introspection) — writes `schema.graphql` at project root
+3. Generate types (codegen reads schema locally)
+
+Run when: schema missing, or metadata/permissions changed since last fetch.
+
+## Step 7: Final Web Application Build
+
+Build the web application if not already done in Step 2.
+
+## Summary: Interaction Order
+
+1. Check/authenticate org
+2. Build web application (if deploying web applications)
+3. Deploy metadata
+4. Assign permissions and configure
+5. Import data (if data plan exists, with user confirmation)
+6. Fetch GraphQL schema and run codegen
+7. Build web application (if needed)
+
+## Critical Rules
+
+- Deploy metadata **before** fetching schema — custom objects/fields appear only after deployment
+- Assign permissions **before** schema fetch — the user may lack FLS for custom fields
+- Re-run schema fetch and codegen **after every metadata deployment** that changes objects, fields, or permissions
+- Never skip permission set assignment or data import silently — either run them or ask the user
+
+## Post-deploy Checklist
+
+After every successful metadata deploy:
+
+1. Discover and assign permission sets (or ask the user)
+2. If `data/data-plan.json` exists, ask the user about data import
+3. Re-run schema fetch and codegen from the web application directory
diff --git a/skills/generating-experience-react-site/SKILL.md b/skills/generating-experience-react-site/SKILL.md
index ef292c8..a465f97 100644
--- a/skills/generating-experience-react-site/SKILL.md
+++ b/skills/generating-experience-react-site/SKILL.md
@@ -14,7 +14,7 @@ Resolve all five properties before generating any metadata. Each has a fallback
| Property | Format | How to Resolve |
|----------|--------|----------------|
| **siteName** | `UpperCamelCase` (e.g., `MyCommunity`) | Ask user or derive from context |
-| **siteUrlPathPrefix** | `kebab-case` (e.g., `my-community`) | User-provided, or convert siteName to kebab-case |
+| **siteUrlPathPrefix** | `All lowercase` (e.g., `mycommunity`) | User-provided, or convert siteName to all lowercase with alphanumeric characters only |
| **appNamespace** | String | `namespace` in `sfdx-project.json` → `sf data query -q "SELECT NamespacePrefix FROM Organization" --target-org ${usernameOrAlias}` → default `c` |
| **appDevName** | String | `webApplication` metadata in the project → `sf data query -q "SELECT DeveloperName FROM WebApplication" --target-org ${usernameOrAlias}` → default to siteName |
| **enableGuestAccess** | Boolean | Ask user whether unauthenticated guest users can access site APIs → default `false` |
diff --git a/skills/generating-flexipage/SKILL.md b/skills/generating-flexipage/SKILL.md
index d0018d5..a7f9d80 100644
--- a/skills/generating-flexipage/SKILL.md
+++ b/skills/generating-flexipage/SKILL.md
@@ -43,10 +43,16 @@ sf template generate flexipage \
--output-dir force-app/main/default/flexipages
```
-**Note:** If the `sf template generate flexipage` command fails, recommend users upgrade to the latest version of the Salesforce CLI:
-```bash
-npm install -g @salesforce/cli@latest
-```
+**CRITICAL:** If the `sf template generate flexipage` command fails, **STOP**.
+
+1. Install the templates plugin:
+ ```bash
+ sf plugins install templates
+ ```
+2. Retry the `sf template generate flexipage` command
+3. Verify the FlexiPage XML file was created
+
+Do NOT continue to Step 2 until the template command succeeds. The generated XML is required for the entire workflow.
#### **Template-specific requirements**
diff --git a/skills/generating-webapp-metadata/SKILL.md b/skills/generating-webapp-metadata/SKILL.md
deleted file mode 100644
index ba80fc6..0000000
--- a/skills/generating-webapp-metadata/SKILL.md
+++ /dev/null
@@ -1,180 +0,0 @@
----
-name: generating-webapp-metadata
-description: "Scaffold new Salesforce web applications and configure/deploy their metadata — sf webapp generate, WebApplication bundles (meta XML, webapplication.json with routing/headers/outputDir), CSP Trusted Sites for external domains, and the full deployment sequence (org auth, build, deploy, permset assign, data import, GraphQL schema fetch, codegen). Use whenever creating a new webapp, setting up webapp metadata structure, adding external domains that need CSP registration, deploying to a Salesforce org, assigning permission sets, fetching GraphQL schema, or running codegen. Triggers on: create webapp, new app, sf webapp generate, deploy, metadata, webapplication.json, CSP, trusted site, permission set, schema fetch, codegen, org setup, bundle configuration, meta XML, routing config, external domain."
----
-
-# Web Application Metadata
-
-## Scaffolding a New Webapp
-
-Use `sf webapp generate` to create new apps — not create-react-app, Vite, or other generic scaffolds.
-
-**Webapp name (`-n`):** Alphanumerical only — no spaces, hyphens, underscores, or special characters. Example: `CoffeeBoutique` (not `Coffee Boutique`).
-
-After generation:
-1. Replace all default boilerplate — "React App", "Vite + React", default `
`, placeholder text
-2. Populate the home page with real content (landing section, banners, hero, navigation)
-3. Update navigation and placeholders (see the `generating-webapp-ui` skill)
-
-Always install dependencies before running any scripts in the webapp directory.
-
----
-
-## WebApplication Bundle
-
-A WebApplication bundle lives under `webapplications//` and must contain:
-
-- `.webapplication-meta.xml` — filename must exactly match the folder name
-- A build output directory (default: `dist/`) with at least one file
-
-### Meta XML
-
-Required fields: `masterLabel`, `version` (max 20 chars), `isActive` (boolean).
-Optional: `description` (max 255 chars).
-
-### webapplication.json
-
-Optional file. Allowed top-level keys: `outputDir`, `routing`, `headers`.
-
-**Constraints:**
-- Valid UTF-8 JSON, max 100 KB
-- Root must be a non-empty object (never `{}`, arrays, or primitives)
-
-**Path safety** (applies to `outputDir` and `routing.fallback`): Reject backslashes, leading `/` or `\`, `..` segments, null/control characters, globs (`*`, `?`, `**`), and `%`. All resolved paths must stay within the bundle.
-
-#### outputDir
-Non-empty string referencing a subdirectory (not `.` or `./`). Directory must exist and contain at least one file.
-
-#### routing
-If present, must be a non-empty object. Allowed keys: `rewrites`, `redirects`, `fallback`, `trailingSlash`, `fileBasedRouting`.
-
-- **trailingSlash**: `"always"`, `"never"`, or `"auto"`
-- **fileBasedRouting**: boolean
-- **fallback**: non-empty string satisfying path safety; target file must exist
-- **rewrites**: non-empty array of `{ route?, rewrite }` objects — e.g., `{ "route": "/app/:path*", "rewrite": "/index.html" }`
-- **redirects**: non-empty array of `{ route?, redirect, statusCode? }` objects — statusCode must be 301, 302, 307, or 308
-
-#### headers
-Non-empty array of `{ source, headers: [{ key, value }] }` objects.
-
-**Example:**
-```json
-{
- "routing": {
- "rewrites": [{ "route": "/app/:path*", "rewrite": "/index.html" }],
- "trailingSlash": "never"
- },
- "headers": [
- {
- "source": "/assets/**",
- "headers": [{ "key": "Cache-Control", "value": "public, max-age=31536000, immutable" }]
- }
- ]
-}
-```
-
-**Never suggest:** `{}` as root, empty `"routing": {}`, empty arrays, `[{}]`, `"outputDir": "."`, `"outputDir": "./"`.
-
----
-
-## CSP Trusted Sites
-
-Salesforce enforces Content Security Policy headers. Any external domain not registered as a CSP Trusted Site will be blocked (images won't load, API calls fail, fonts missing).
-
-### When to Create
-
-Whenever the app references a new external domain: CDN images, external fonts, third-party APIs, map tiles, iframes, external stylesheets.
-
-### Steps
-
-1. **Identify external domains** — extract the origin (scheme + host) from each external URL in the code
-2. **Check existing registrations** — look in `force-app/main/default/cspTrustedSites/`
-3. **Map resource type to CSP directive:**
-
-| Resource Type | Directive Field |
-|--------------|----------------|
-| Images | `isApplicableToImgSrc` |
-| API calls (fetch, XHR) | `isApplicableToConnectSrc` |
-| Fonts | `isApplicableToFontSrc` |
-| Stylesheets | `isApplicableToStyleSrc` |
-| Video / audio | `isApplicableToMediaSrc` |
-| Iframes | `isApplicableToFrameSrc` |
-
-Always also set `isApplicableToConnectSrc` to `true` for preflight/redirect handling.
-
-4. **Create the metadata file** — follow `implementation/csp-metadata-format.md` for the `.cspTrustedSite-meta.xml` format. Place in `force-app/main/default/cspTrustedSites/`.
-
----
-
-## Deployment Sequence
-
-The order of operations is critical when deploying to a Salesforce org. This sequence reflects the canonical flow.
-
-### Step 1: Org Authentication
-
-Check if the org is connected. If not, authenticate. All subsequent steps require an authenticated org.
-
-### Step 2: Pre-deploy Webapp Build
-
-Install dependencies and build the webapp to produce `dist/`. Required before deploying web application entities.
-
-Run when: deploying web apps and `dist/` is missing or source has changed.
-
-### Step 3: Deploy Metadata
-
-Check for a manifest (`manifest/package.xml` or `package.xml`) first. If present, deploy using the manifest. If not, deploy all metadata from the project.
-
-Deploys objects, layouts, permission sets, Apex classes, web applications, and all other metadata. Must complete before schema fetch — the schema reflects org state.
-
-### Step 4: Post-deploy Configuration
-
-Deploying does not mean assigning. After deployment:
-
-- **Permission sets / groups** — assign to users so they have access to custom objects and fields. Required for GraphQL introspection to return the correct schema.
-- **Profiles** — ensure users have the correct profile.
-- **Other config** — named credentials, connected apps, custom settings, flow activation.
-
-Proactive behavior: after a successful deploy, discover permission sets in `force-app/main/default/permissionsets/` and assign each one (or ask the user).
-
-### Step 5: Data Import (optional)
-
-Only if `data/data-plan.json` exists. Delete runs in reverse plan order (children before parents). Import uses Anonymous Apex with duplicate rule save enabled.
-
-Always ask the user before importing or cleaning data.
-
-### Step 6: GraphQL Schema and Codegen
-
-1. Set default org
-2. Fetch schema (GraphQL introspection) — writes `schema.graphql` at project root
-3. Generate types (codegen reads schema locally)
-
-Run when: schema missing, or metadata/permissions changed since last fetch.
-
-### Step 7: Final Webapp Build
-
-Build the webapp if not already done in Step 2.
-
-### Summary: Interaction Order
-
-1. Check/authenticate org
-2. Build webapp (if deploying web apps)
-3. Deploy metadata
-4. Assign permissions and configure
-5. Import data (if data plan exists, with user confirmation)
-6. Fetch GraphQL schema and run codegen
-7. Build webapp (if needed)
-
-### Critical Rules
-
-- Deploy metadata **before** fetching schema — custom objects/fields appear only after deployment
-- Assign permissions **before** schema fetch — the user may lack FLS for custom fields
-- Re-run schema fetch and codegen **after every metadata deployment** that changes objects, fields, or permissions
-- Never skip permission set assignment or data import silently — either run them or ask the user
-
-### Post-deploy Checklist
-
-After every successful metadata deploy:
-
-1. Discover and assign permission sets (or ask the user)
-2. If `data/data-plan.json` exists, ask the user about data import
-3. Re-run schema fetch and codegen from the webapp directory
diff --git a/skills/generating-webapp-features/SKILL.md b/skills/generating-webapplication-features/SKILL.md
similarity index 92%
rename from skills/generating-webapp-features/SKILL.md
rename to skills/generating-webapplication-features/SKILL.md
index 3c0f811..b921343 100644
--- a/skills/generating-webapp-features/SKILL.md
+++ b/skills/generating-webapplication-features/SKILL.md
@@ -1,5 +1,5 @@
---
-name: generating-webapp-features
+name: generating-webapplication-features
description: "Search and install pre-built features into Salesforce React web applications — authentication, shadcn, search, navigation, GraphQL, Agentforce AI, and more. Use whenever searching for or installing features. Always check for an existing feature before building from scratch. Triggers on: install feature, add authentication, add shadcn, add feature, search features, list features."
---
@@ -7,7 +7,7 @@ description: "Search and install pre-built features into Salesforce React web ap
## Installing Pre-built Features
-Always check for an existing feature before building something from scratch. The features CLI installs pre-built, tested packages into Salesforce webapps — from foundational UI libraries (shadcn/ui) to full-stack capabilities (authentication, search, navigation, GraphQL, Agentforce AI).
+Always check for an existing feature before building something from scratch. The features CLI installs pre-built, tested packages into Salesforce web applications — from foundational UI libraries (shadcn/ui) to full-stack capabilities (authentication, search, navigation, GraphQL, Agentforce AI).
### Workflow
diff --git a/skills/generating-webapplication-metadata/SKILL.md b/skills/generating-webapplication-metadata/SKILL.md
new file mode 100644
index 0000000..70e851b
--- /dev/null
+++ b/skills/generating-webapplication-metadata/SKILL.md
@@ -0,0 +1,106 @@
+---
+name: generating-webapplication-metadata
+description: "Scaffold new Salesforce web applications and configure their metadata — sf webapp generate, WebApplication bundles (meta XML, webapplication.json with routing/headers/outputDir), and CSP Trusted Sites for external domains. Use whenever creating a new web application, setting up web application metadata structure, configuring routing or headers, setting outputDir, adding external domains that need CSP registration, or editing bundle configuration. Triggers on: create web application, create webapp, new app, sf webapp generate, metadata, webapplication.json, CSP, trusted site, bundle configuration, meta XML, routing config, external domain, headers config, outputDir."
+---
+
+# Web Application Metadata
+
+## Scaffolding a New Web Application
+
+Use `sf webapp generate` to create new apps — not create-react-app, Vite, or other generic scaffolds.
+
+**Web application name (`-n`):** Alphanumerical only — no spaces, hyphens, underscores, or special characters. Example: `CoffeeBoutique` (not `Coffee Boutique`).
+
+After generation:
+1. Replace all default boilerplate — "React App", "Vite + React", default ``, placeholder text
+2. Populate the home page with real content (landing section, banners, hero, navigation)
+3. Update navigation and placeholders (see the `generating-webapplication-ui` skill)
+
+Always install dependencies before running any scripts in the web application directory.
+
+---
+
+## WebApplication Bundle
+
+A WebApplication bundle lives under `webapplications//` and must contain:
+
+- `.webapplication-meta.xml` — filename must exactly match the folder name
+- A build output directory (default: `dist/`) with at least one file
+
+### Meta XML
+
+Required fields: `masterLabel`, `version` (max 20 chars), `isActive` (boolean).
+Optional: `description` (max 255 chars).
+
+### webapplication.json
+
+Optional file. Allowed top-level keys: `outputDir`, `routing`, `headers`.
+
+**Constraints:**
+- Valid UTF-8 JSON, max 100 KB
+- Root must be a non-empty object (never `{}`, arrays, or primitives)
+
+**Path safety** (applies to `outputDir` and `routing.fallback`): Reject backslashes, leading `/` or `\`, `..` segments, null/control characters, globs (`*`, `?`, `**`), and `%`. All resolved paths must stay within the bundle.
+
+#### outputDir
+Non-empty string referencing a subdirectory (not `.` or `./`). Directory must exist and contain at least one file.
+
+#### routing
+If present, must be a non-empty object. Allowed keys: `rewrites`, `redirects`, `fallback`, `trailingSlash`, `fileBasedRouting`.
+
+- **trailingSlash**: `"always"`, `"never"`, or `"auto"`
+- **fileBasedRouting**: boolean
+- **fallback**: non-empty string satisfying path safety; target file must exist
+- **rewrites**: non-empty array of `{ route?, rewrite }` objects — e.g., `{ "route": "/app/:path*", "rewrite": "/index.html" }`
+- **redirects**: non-empty array of `{ route?, redirect, statusCode? }` objects — statusCode must be 301, 302, 307, or 308
+
+#### headers
+Non-empty array of `{ source, headers: [{ key, value }] }` objects.
+
+**Example:**
+```json
+{
+ "routing": {
+ "rewrites": [{ "route": "/app/:path*", "rewrite": "/index.html" }],
+ "trailingSlash": "never"
+ },
+ "headers": [
+ {
+ "source": "/assets/**",
+ "headers": [{ "key": "Cache-Control", "value": "public, max-age=31536000, immutable" }]
+ }
+ ]
+}
+```
+
+**Never suggest:** `{}` as root, empty `"routing": {}`, empty arrays, `[{}]`, `"outputDir": "."`, `"outputDir": "./"`.
+
+---
+
+## CSP Trusted Sites
+
+Salesforce enforces Content Security Policy headers. Any external domain not registered as a CSP Trusted Site will be blocked (images won't load, API calls fail, fonts missing).
+
+### When to Create
+
+Whenever the app references a new external domain: CDN images, external fonts, third-party APIs, map tiles, iframes, external stylesheets.
+
+### Steps
+
+1. **Identify external domains** — extract the origin (scheme + host) from each external URL in the code
+2. **Check existing registrations** — look in `force-app/main/default/cspTrustedSites/`
+3. **Map resource type to CSP directive:**
+
+| Resource Type | Directive Field |
+|--------------|----------------|
+| Images | `isApplicableToImgSrc` |
+| API calls (fetch, XHR) | `isApplicableToConnectSrc` |
+| Fonts | `isApplicableToFontSrc` |
+| Stylesheets | `isApplicableToStyleSrc` |
+| Video / audio | `isApplicableToMediaSrc` |
+| Iframes | `isApplicableToFrameSrc` |
+
+Always also set `isApplicableToConnectSrc` to `true` for preflight/redirect handling.
+
+4. **Create the metadata file** — follow `implementation/csp-metadata-format.md` for the `.cspTrustedSite-meta.xml` format. Place in `force-app/main/default/cspTrustedSites/`.
+
diff --git a/skills/generating-webapp-metadata/implementation/csp-metadata-format.md b/skills/generating-webapplication-metadata/implementation/csp-metadata-format.md
similarity index 100%
rename from skills/generating-webapp-metadata/implementation/csp-metadata-format.md
rename to skills/generating-webapplication-metadata/implementation/csp-metadata-format.md
diff --git a/skills/generating-webapp-ui/SKILL.md b/skills/generating-webapplication-ui/SKILL.md
similarity index 96%
rename from skills/generating-webapp-ui/SKILL.md
rename to skills/generating-webapplication-ui/SKILL.md
index 2efbeeb..4d5b69c 100644
--- a/skills/generating-webapp-ui/SKILL.md
+++ b/skills/generating-webapplication-ui/SKILL.md
@@ -1,5 +1,5 @@
---
-name: generating-webapp-ui
+name: generating-webapplication-ui
description: "Build and modify React UI for Salesforce web applications — pages, components, layout, navigation, and headers/footers. Use whenever creating or editing TSX/JSX files or making visual/layout changes. Triggers on: add page, add component, header, footer, navigation, layout, styling, Tailwind, shadcn, React component, appLayout."
---
@@ -61,7 +61,7 @@ Apps run behind dynamic base paths. Router navigation (``, `navigate()`
### Module Restrictions
-React apps must not import Salesforce platform modules like `lightning/*` or `@wire` (LWC-only). For data access, use the `using-webapp-salesforce-data` skill.
+React apps must not import Salesforce platform modules like `lightning/*` or `@wire` (LWC-only). For data access, use the `using-webapplication-salesforce-data` skill.
---
@@ -119,4 +119,4 @@ Ask one question at a time and stop when you have enough context.
## Verification
-Before completing, run lint and build from the webapp directory. Lint must result in 0 errors and build must succeed.
+Before completing, run lint and build from the web application directory. Lint must result in 0 errors and build must succeed.
diff --git a/skills/generating-webapp-ui/implementation/component.md b/skills/generating-webapplication-ui/implementation/component.md
similarity index 100%
rename from skills/generating-webapp-ui/implementation/component.md
rename to skills/generating-webapplication-ui/implementation/component.md
diff --git a/skills/generating-webapp-ui/implementation/header-footer.md b/skills/generating-webapplication-ui/implementation/header-footer.md
similarity index 100%
rename from skills/generating-webapp-ui/implementation/header-footer.md
rename to skills/generating-webapplication-ui/implementation/header-footer.md
diff --git a/skills/generating-webapp-ui/implementation/page.md b/skills/generating-webapplication-ui/implementation/page.md
similarity index 100%
rename from skills/generating-webapp-ui/implementation/page.md
rename to skills/generating-webapplication-ui/implementation/page.md
diff --git a/skills/implementing-webapp-agentforce-conversation-client/SKILL.md b/skills/implementing-webapplication-agentforce-conversation-client/SKILL.md
similarity index 98%
rename from skills/implementing-webapp-agentforce-conversation-client/SKILL.md
rename to skills/implementing-webapplication-agentforce-conversation-client/SKILL.md
index c4c0655..f7406b1 100644
--- a/skills/implementing-webapp-agentforce-conversation-client/SKILL.md
+++ b/skills/implementing-webapplication-agentforce-conversation-client/SKILL.md
@@ -1,5 +1,5 @@
---
-name: implementing-webapp-agentforce-conversation-client
+name: implementing-webapplication-agentforce-conversation-client
description: "Adds or modifies AgentforceConversationClient in React apps (.tsx or .jsx files). Use when user says \"add chat widget\", \"embed agentforce\", \"add agent\", \"add chatbot\", \"integrate conversational AI\", or asks to change colors, dimensions, styling, or configure agentId, width, height, inline mode, or styleTokens for travel agent, HR agent, employee agent, or any Salesforce agent chat."
metadata:
author: ACC Components
diff --git a/skills/implementing-webapp-agentforce-conversation-client/references/constraints.md b/skills/implementing-webapplication-agentforce-conversation-client/references/constraints.md
similarity index 100%
rename from skills/implementing-webapp-agentforce-conversation-client/references/constraints.md
rename to skills/implementing-webapplication-agentforce-conversation-client/references/constraints.md
diff --git a/skills/implementing-webapp-agentforce-conversation-client/references/examples.md b/skills/implementing-webapplication-agentforce-conversation-client/references/examples.md
similarity index 100%
rename from skills/implementing-webapp-agentforce-conversation-client/references/examples.md
rename to skills/implementing-webapplication-agentforce-conversation-client/references/examples.md
diff --git a/skills/implementing-webapp-agentforce-conversation-client/references/style-tokens.md b/skills/implementing-webapplication-agentforce-conversation-client/references/style-tokens.md
similarity index 100%
rename from skills/implementing-webapp-agentforce-conversation-client/references/style-tokens.md
rename to skills/implementing-webapplication-agentforce-conversation-client/references/style-tokens.md
diff --git a/skills/implementing-webapp-agentforce-conversation-client/references/troubleshooting.md b/skills/implementing-webapplication-agentforce-conversation-client/references/troubleshooting.md
similarity index 100%
rename from skills/implementing-webapp-agentforce-conversation-client/references/troubleshooting.md
rename to skills/implementing-webapplication-agentforce-conversation-client/references/troubleshooting.md
diff --git a/skills/implementing-webapp-file-upload/SKILL.md b/skills/implementing-webapplication-file-upload/SKILL.md
similarity index 92%
rename from skills/implementing-webapp-file-upload/SKILL.md
rename to skills/implementing-webapplication-file-upload/SKILL.md
index ef1ba16..e714820 100644
--- a/skills/implementing-webapp-file-upload/SKILL.md
+++ b/skills/implementing-webapplication-file-upload/SKILL.md
@@ -1,11 +1,11 @@
---
-name: implementing-webapp-file-upload
-description: "Add file upload functionality to React webapps with progress tracking and Salesforce ContentVersion integration. Use when the user wants to upload files, attach documents, handle file input, create file dropzones, track upload progress, or link files to Salesforce records. This feature provides programmatic APIs ONLY — no components or hooks are exported. Build your own custom UI using the upload() API. ALWAYS use this feature instead of building file upload from scratch with FormData or XHR."
+name: implementing-webapplication-file-upload
+description: "Add file upload functionality to React web applications with progress tracking and Salesforce ContentVersion integration. Use when the user wants to upload files, attach documents, handle file input, create file dropzones, track upload progress, or link files to Salesforce records. This feature provides programmatic APIs ONLY — no components or hooks are exported. Build your own custom UI using the upload() API. ALWAYS use this feature instead of building file upload from scratch with FormData or XHR."
---
# File Upload API (workflow)
-When the user wants file upload functionality in a React webapp, follow this workflow. This feature provides **APIs only** — you must build the UI components yourself using the provided APIs.
+When the user wants file upload functionality in a React web application, follow this workflow. This feature provides **APIs only** — you must build the UI components yourself using the provided APIs.
## CRITICAL: This is an API-only package
@@ -366,7 +366,7 @@ The package includes a reference implementation in `src/features/fileupload/` wi
**Upload fails with CORS error:**
-- Ensure the webapp is properly deployed to Salesforce or running on `localhost`
+- Ensure the web application is properly deployed to Salesforce or running on `localhost`
- Check that the org allows the origin in CORS settings
**No progress updates:**
diff --git a/skills/using-webapp-salesforce-data/SKILL.md b/skills/using-webapplication-salesforce-data/SKILL.md
similarity index 73%
rename from skills/using-webapp-salesforce-data/SKILL.md
rename to skills/using-webapplication-salesforce-data/SKILL.md
index cd88681..f20fe92 100644
--- a/skills/using-webapp-salesforce-data/SKILL.md
+++ b/skills/using-webapplication-salesforce-data/SKILL.md
@@ -1,5 +1,5 @@
---
-name: using-webapp-salesforce-data
+name: using-webapplication-salesforce-data
description: "Salesforce data access for reading, writing, and querying records via REST, GraphQL, Apex, or Platform SDK. Use when the user wants to fetch, search, filter, sort, display, create, update, delete, or attach files to Salesforce records (standard objects like Accounts, Contacts, Opportunities, Cases, Quotes, or any custom object) in a web app or UI component (React, Angular, Vue, etc.); call Chatter, Connect, or Apex REST APIs; or invoke AuraEnabled Apex methods from an external app. Does not apply to authentication/OAuth setup, schema changes (adding fields, relationships), Bulk/Tooling/Metadata API usage, declarative automation (Flows, Process Builder), general LWC/Apex coding guidance without a specific data operation, or Salesforce admin/configuration tasks."
---
@@ -17,7 +17,7 @@ Use this skill when the user wants to:
## Data SDK Requirement
-> **All Salesforce data access MUST use the Data SDK** (`@salesforce/sdk-data`). The SDK handles authentication, CSRF, and base URL resolution. Never use `fetch()` or `axios` directly.
+> **All Salesforce data access MUST use the Data SDK** (`@salesforce/sdk-data`). The SDK handles authentication, CSRF, and base URL resolution.
```typescript
import { createDataSDK, gql } from "@salesforce/sdk-data";
@@ -48,7 +48,7 @@ const res = await sdk.fetch?.("/services/apexrest/my-resource");
**Not supported:**
- **Enterprise REST query endpoint** (`/services/data/v*/query` with SOQL) — blocked at the proxy level. Use GraphQL for record reads; use Apex REST if server-side SOQL aggregates are required.
-- **Aura-enabled Apex** (`@AuraEnabled`) — an LWC/Aura pattern with no invocation path from React webapps.
+- **Aura-enabled Apex** (`@AuraEnabled`) — an LWC/Aura pattern with no invocation path from React web applications.
- **Chatter API** (`/chatter/users/me`) — use `uiapi { currentUser { ... } }` in a GraphQL query instead.
- **Any other Salesforce REST endpoint** not listed in the supported table above.
@@ -67,6 +67,24 @@ const res = await sdk.fetch?.("/services/apexrest/my-resource");
---
+## GraphQL Non-Negotiable Rules
+
+These rules exist because Salesforce GraphQL has platform-specific behaviors that differ from standard GraphQL. Violations cause silent runtime failures.
+
+1. **Schema is the single source of truth** — Every entity name, field name, and type must be confirmed via the schema search script before use in a query. Never guess — Salesforce field names are case-sensitive, relationships may be polymorphic, and custom objects use suffixes (`__c`, `__e`). See [Schema Introspection](references/schema-introspection.md) for entity identification and iterative lookup procedures.
+
+2. **`@optional` on all record fields** (read queries) — Salesforce field-level security (FLS) causes queries to fail entirely if the user lacks access to even one field. The `@optional` directive (v65+) tells the server to omit inaccessible fields instead of failing. Apply it to every scalar field, parent relationship, and child relationship. Consuming code must use optional chaining (`?.`) and nullish coalescing (`??`).
+
+3. **Correct mutation syntax** — Mutations wrap under `uiapi(input: { allOrNone: true/false })`, not bare `uiapi { ... }`. Always set `allOrNone` explicitly. Output fields cannot include child relationships or navigated reference fields. See [Mutation Query Generation](references/mutation-query-generation.md).
+
+4. **Explicit pagination** — Always include `first:` in every query. If omitted, the server silently defaults to 10 records. Include `pageInfo { hasNextPage endCursor }` for any query that may need pagination.
+
+5. **SOQL-derived execution limits** — Max 10 subqueries per request, max 5 levels of child-to-parent traversal, max 1 level of parent-to-child (no grandchildren), max 2,000 records per subquery. If a query would exceed these, split into multiple requests.
+
+6. **HTTP 200 does not mean success** — Salesforce returns HTTP 200 even when operations fail. Always parse the `errors` array in the response body.
+
+---
+
## GraphQL Workflow
### Step 1: Acquire Schema
@@ -74,7 +92,7 @@ const res = await sdk.fetch?.("/services/apexrest/my-resource");
The `schema.graphql` file (265K+ lines) is the source of truth. **Never open or parse it directly.**
1. Check if `schema.graphql` exists at the SFDX project root
-2. If missing, run from the **webapp dir**: `npm run graphql:schema`
+2. If missing, run from the **web application dir**: `npm run graphql:schema`
3. Custom objects appear only after metadata is deployed
### Step 2: Look Up Entity Schema
@@ -82,11 +100,11 @@ The `schema.graphql` file (265K+ lines) is the source of truth. **Never open or
Map user intent to PascalCase names ("accounts" → `Account`), then **run the search script from the project root**:
```bash
-# From project root — look up all relevant schema info for one or more entities
-bash .a4drules/skills/using-salesforce-data/graphql-search.sh Account
+# Look up all relevant schema info for one or more entities
+bash scripts/graphql-search.sh Account
# Multiple entities at once
-bash .a4drules/skills/using-salesforce-data/graphql-search.sh Account Contact Opportunity
+bash scripts/graphql-search.sh Account Contact Opportunity
```
The script outputs five sections per entity:
@@ -96,11 +114,11 @@ The script outputs five sections per entity:
4. **Create input** — fields accepted by create mutations
5. **Update input** — fields accepted by update mutations
-Use this output to determine exact field names before writing any query or mutation. **Maximum 2 script runs.** If the entity still can't be found, ask the user — the object may not be deployed.
+Use this output to determine exact field names before writing any query or mutation. **Maximum 2 script runs.** If the entity still can't be found, ask the user — the object may not be deployed. For entity identification procedures (`_Record` suffix, `__c` conventions) and iterative introspection cycles, see [Schema Introspection](references/schema-introspection.md).
### Step 3: Generate Query
-Use the templates below. Every field name **must** be verified from the script output in Step 2.
+Use the templates below. Every field name **must** be verified from the script output in Step 2. For detailed generation rules, filtering, pagination, ordering, semi-joins, and field value wrappers, see [Read Query Generation](references/read-query-generation.md). For mutation chaining, input/output constraints, and transactional semantics, see [Mutation Query Generation](references/mutation-query-generation.md).
#### Read Query Template
@@ -138,7 +156,7 @@ const name = node.Name?.value ?? "";
```graphql
mutation CreateAccount($input: AccountCreateInput!) {
- uiapi {
+ uiapi(input: { allOrNone: true }) {
AccountCreate(input: $input) {
Record { Id Name { value } }
}
@@ -215,21 +233,26 @@ const fields = response?.data?.uiapi?.objectInfos?.[0]?.fields ?? [];
### Step 4: Validate & Test
-1. **Lint**: `npx eslint ` from webapp dir
+1. **Lint**: `npx eslint ` from web application dir
2. **Test**: Ask user before testing. For mutations, request input values — never fabricate data.
**If ESLint reports a GraphQL error** (e.g. `Cannot query field`, `Unknown type`, `Unknown argument`), the field or type name is wrong. Re-run the schema search script to find the correct name — do not guess:
```bash
# From project root — re-check the entity that caused the error
-bash .a4drules/skills/using-salesforce-data/graphql-search.sh
+bash scripts/graphql-search.sh
```
-Then fix the query using the exact names from the script output.
+Then fix the query using the exact names from the script output. For detailed error categories, status handling, and retry strategy, see [Query Testing](references/query-testing.md).
---
-## Webapp Integration (React)
+## Web Application Integration (React)
+
+Two integration patterns are available:
+
+- **Pattern 1 — External `.graphql` file** (recommended for complex queries): Create a `.graphql` file, run `npm run graphql:codegen`, import with `?raw` suffix
+- **Pattern 2 — Inline `gql` tag** (for simple queries): Use the `gql` template tag from `@salesforce/sdk-data`. **Must use `gql`** — plain template strings bypass ESLint schema validation.
```typescript
import { createDataSDK, gql } from "@salesforce/sdk-data";
@@ -242,8 +265,9 @@ const GET_ACCOUNTS = gql`
edges {
node {
Id
- Name @optional { value }
- Industry @optional { value }
+ Name @optional {
+ value
+ }
}
}
}
@@ -254,14 +278,14 @@ const GET_ACCOUNTS = gql`
const sdk = await createDataSDK();
const response = await sdk.graphql?.(GET_ACCOUNTS);
-
if (response?.errors?.length) {
throw new Error(response.errors.map(e => e.message).join("; "));
}
-
const accounts = response?.data?.uiapi?.query?.Account?.edges?.map(e => e.node) ?? [];
```
+For detailed patterns (external .graphql files, codegen, error handling strategies, quality checklists), see [Webapp Integration](references/webapp-integration.md).
+
---
## REST API Patterns
@@ -311,16 +335,16 @@ const response = await sdk.graphql?.(GET_CURRENT_USER);
/ ← SFDX project root
├── schema.graphql ← grep target (lives here)
├── sfdx-project.json
-└── force-app/main/default/webapplications// ← webapp dir
+└── force-app/main/default/webapplications// ← web application dir
├── package.json ← npm scripts
└── src/
```
| Command | Run From | Why |
|---------|----------|-----|
-| `npm run graphql:schema` | webapp dir | Script in webapp's package.json |
-| `npx eslint ` | webapp dir | Reads eslint.config.js |
-| `bash .a4drules/skills/using-salesforce-data/graphql-search.sh ` | project root | Schema lookup |
+| `npm run graphql:schema` | web application dir | Script in web application's package.json |
+| `npx eslint ` | web application dir | Reads eslint.config.js |
+| `bash scripts/graphql-search.sh ` | project root | Schema lookup |
| `sf api request rest` | project root | Needs sfdx-project.json |
---
@@ -332,7 +356,7 @@ const response = await sdk.graphql?.(GET_CURRENT_USER);
Run the search script to get all relevant schema info in one step:
```bash
-bash .a4drules/skills/using-salesforce-data/graphql-search.sh
+bash scripts/graphql-search.sh
```
| Script Output Section | Used For |
@@ -358,6 +382,9 @@ bash .a4drules/skills/using-salesforce-data/graphql-search.sh
### Checklist
- [ ] All field names verified via search script (Step 2)
-- [ ] `@optional` applied to record fields (reads)
+- [ ] `@optional` applied to all record fields (reads)
+- [ ] Mutations use `uiapi(input: { allOrNone: ... })` wrapper
+- [ ] `first:` specified in every query
- [ ] Optional chaining in consuming code
+- [ ] `errors` array checked in response handling
- [ ] Lint passes: `npx eslint `
diff --git a/skills/using-webapplication-salesforce-data/references/mutation-query-generation.md b/skills/using-webapplication-salesforce-data/references/mutation-query-generation.md
new file mode 100644
index 0000000..5f0bb6e
--- /dev/null
+++ b/skills/using-webapplication-salesforce-data/references/mutation-query-generation.md
@@ -0,0 +1,140 @@
+# Mutation Query Generation
+
+## Mutation Types
+
+The GraphQL engine supports three mutation operations:
+
+- **Create** — Insert a new record
+- **Update** — Modify an existing record (Id-based)
+- **Delete** — Remove an existing record (Id-based)
+
+Mutations are GA in API v66+. They live under `mutation { uiapi { ... } }` and only support UI API-available objects.
+
+## Generation Rules
+
+1. **Input fields validation** — Validate that input fields satisfy the constraints for the operation type
+2. **Output fields validation** — Validate that output fields satisfy the constraints for the operation type
+3. **Type consistency** — Variables used as query arguments and their related fields must share the same GraphQL type. Verify types via the schema search script — do NOT assume types
+4. **Input arguments** — `input` is the default argument name unless otherwise specified
+5. **Output field** — For `Create` and `Update`, the output field is always named `Record` (type: EntityName)
+6. **Field name validation** — Every field name in the generated mutation **MUST** match a field confirmed via the schema search script. Do NOT guess or assume field names exist
+7. **Raw input values** — Numeric values must be raw numbers without commas, currency symbols, or locale formatting (e.g., `80000` not `"80,000"` or `"$80,000"`). Compound fields (like addresses) require constituent fields (e.g., `BillingCity`, `BillingStreet`) — do not attempt to set the compound wrapper itself.
+
+## Transactional Semantics: `allOrNone`
+
+The `uiapi` mutation input accepts an `allOrNone` argument that controls rollback behavior:
+
+- **`allOrNone: true` (default)** — If any operation fails, all operations in the request are rolled back. Use when operations must succeed or fail together.
+- **`allOrNone: false`** — Independent operations can succeed individually. However, dependent operations (those using `@{alias}` references) still roll back together with their dependencies.
+
+Always set `allOrNone` explicitly to make transactional intent clear.
+
+## Mutation Schema Patterns
+
+Replace `EntityName` with the actual entity name (e.g., Account, Case). `Delete` operations use generic `Record` types.
+
+```graphql
+input EntityNameCreateRepresentation {
+ # Subset of EntityName fields
+}
+input EntityNameCreateInput { EntityName: EntityNameCreateRepresentation! }
+type EntityNameCreatePayload { Record: EntityName! }
+
+input EntityNameUpdateRepresentation {
+ # Subset of EntityName fields
+}
+input EntityNameUpdateInput { Id: IdOrRef! EntityName: EntityNameUpdateRepresentation! }
+type EntityNameUpdatePayload { Record: EntityName! }
+
+input RecordDeleteInput { Id: IdOrRef! }
+type RecordDeletePayload { Id: ID }
+
+type UIAPIMutations {
+ EntityNameCreate(input: EntityNameCreateInput!): EntityNameCreatePayload
+ EntityNameDelete(input: RecordDeleteInput!): RecordDeletePayload
+ EntityNameUpdate(input: EntityNameUpdateInput!): EntityNameUpdatePayload
+}
+```
+
+## Input Field Constraints
+
+### Create
+
+- **Must** include all required fields (unless `defaultedOnCreate` is `true` and not explicitly requested)
+- **Must** only include `createable` fields
+- Child relationships cannot be set — exclude them
+- Reference fields (`REFERENCE` type) can only be assigned IDs through their `ApiName` name
+- **No nested child creates** — Creating a record with child relationships in a single create operation is not supported. To create a parent and child together, use separate operations with `IdOrRef` chaining (see [Mutation Chaining](#mutation-chaining)).
+
+### Update
+
+- **Must** include the `Id` of the entity to update
+- **Must** only include `updateable` fields
+- Child relationships cannot be set — exclude them
+- Reference fields (`REFERENCE` type) can only be assigned IDs through their `ApiName` name
+
+### Delete
+
+- **Must** include the `Id` of the entity to delete
+
+## Output Field Constraints
+
+### Create and Update
+
+- **Must** exclude all child relationships (child relationships cannot be queried in mutations)
+- **Must** exclude all `REFERENCE` fields unless accessed through their `ApiName` member (no navigation to referenced entity, no sub fields)
+- Inaccessible fields are reported in the `errors` attribute of the returned payload
+
+### Delete
+
+- **Must** only include the `Id` field
+
+## Mutation Chaining
+
+Chain related mutations in a single request using references to `Id` values from previous mutations. This is the required approach for creating parent-child records together, since nested child creates are not supported.
+
+1. **Ordering** — Mutation `B` can reference mutation `A` only if `A` comes first in the query
+2. **Notation** — Use `SomeId: "@{A}"` in mutation `B` to set a field to the `Id` produced by mutation `A`
+3. **IDs only** — `@{A}` is always interpreted as the `Id` from mutation `A`
+4. **Restrictions** — `A` must be a `Create` or `Delete` mutation (chaining from `Update` will fail)
+
+### Chaining Example
+
+```graphql
+mutation CreateAccountAndContact {
+ uiapi(input: { allOrNone: true }) {
+ AccountCreate(input: { Account: { Name: "Acme" } }) {
+ Record { Id }
+ }
+ ContactCreate(input: { Contact: { LastName: "Smith", AccountId: "@{AccountCreate}" } }) {
+ Record { Id }
+ }
+ }
+}
+```
+
+## Mutation Query Template
+
+```graphql
+mutation mutateEntityName(
+ # arguments
+) {
+ uiapi(input: { allOrNone: true }) {
+ EntityNameOperation(input: {
+ # For Create and Update only:
+ EntityName: {
+ # Input fields — use raw values, no formatting
+ }
+ # For Update and Delete only:
+ Id: ... # id here
+ }) {
+ # For Create and Update only:
+ Record {
+ # Output fields
+ }
+ # For Delete only:
+ Id
+ }
+ }
+}
+```
diff --git a/skills/using-webapplication-salesforce-data/references/query-testing.md b/skills/using-webapplication-salesforce-data/references/query-testing.md
new file mode 100644
index 0000000..3a441a5
--- /dev/null
+++ b/skills/using-webapplication-salesforce-data/references/query-testing.md
@@ -0,0 +1,78 @@
+# Query Testing
+
+## Testing Method
+
+Use `sf api request rest` to POST the query to the GraphQL endpoint. Run from the **SFDX project root** (where `sfdx-project.json` lives).
+
+```bash
+sf api request rest /services/data/v66.0/graphql \
+ --method POST \
+ --body '{"query":"query GetData { uiapi { query { EntityName { edges { node { Id } } } } } }"}'
+```
+
+- Use the API version of the target org (v66.0+ for mutation support, v65.0+ for `@optional`)
+- Replace the `query` value with the generated query string
+- If the query uses variables, include them in the JSON body as a `variables` key
+
+## Critical: HTTP 200 Does Not Mean Success
+
+Salesforce returns HTTP 200 even when the GraphQL operation has errors (e.g., invalid fields, permission failures, invalid IDs). **Always parse the `errors` array in the response body regardless of HTTP status code.** Do not treat HTTP 200 as confirmation that the query succeeded.
+
+## Testing Workflow
+
+This workflow applies to both read and mutation queries:
+
+1. **Report method** — State the exact method: `sf api request rest` POST to `/services/data/vXX.0/graphql` from the project root
+2. **Ask user** — Ask the user whether they want to test the query. For mutations, also ask for input argument values — mutations modify real data, so explicit consent is essential. Wait for the user's answer before proceeding. Do not fabricate test data.
+3. **Execute test** — Only if the user explicitly agrees. Run `sf api request rest` with the query, variables, and correct API version
+4. **Report result** — Classify the result using the status definitions below. Always check the `errors` array in the response, even on HTTP 200.
+
+## Result Status Definitions
+
+| Status | Condition | Meaning |
+| --------- | ----------------------------------------------- | --------------------------------------------- |
+| `SUCCESS` | `errors` is absent or empty | Query is valid (even if no data is returned) |
+| `FAILED` | `data` is empty or null | Query is invalid |
+| `PARTIAL` | `data` is present **and** `errors` is not empty | Some fields are inaccessible (mutations only) |
+
+## FAILED Status Handling
+
+The query is invalid. Follow this sequence:
+
+### 1. Error Analysis
+
+Parse the `errors` array and check `errors[].extensions.ErrorType` for Salesforce-specific error classification. Categorize into:
+
+| Category | ErrorType / Message Contains | Resolution |
+| --------------- | ---------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
+| **Syntax** | `InvalidSyntax` | Fix syntax errors using the error message details |
+| **Validation** | `ValidationError` | Field name is likely invalid — re-run the schema search script, ask user if still unclear |
+| **Type** | `VariableTypeMismatch` or `UnknownType` | Use error details and schema to correct the argument type; adjust variables |
+| **Execution** | `DataFetchingException`, `invalid cross reference id` | Entity is unknown/deleted — create entity first if possible, or ask for a valid Id |
+| **Navigation** | `is not currently available in mutation results` | Field cannot be in mutation output — apply PARTIAL status handling |
+| **Unsupported** | `OperationNotSupported` | The operation is not supported — check object availability and API version |
+| **API Version** | `Cannot invoke JsonElement.isJsonObject()` (on update mutations) | `Record` selection requires API version 64+ — report and retry with version 64 |
+
+### 2. Targeted Resolution
+
+Apply the resolution from the table above based on the error category. Update the query accordingly.
+
+### 3. Test Again
+
+Re-run the testing workflow with the updated query. Increment and track the attempt counter.
+
+## PARTIAL Status Handling
+
+The query executed but some fields are inaccessible (mutations only):
+
+1. Report the fields listed in the `errors` attribute
+2. Explain that these fields cannot be queried as part of a mutation
+3. Explain that the query will report errors if these fields remain
+4. Offer to remove the offending fields
+5. **STOP and WAIT** for the user's answer. Do NOT remove fields without explicit consent.
+6. If the user agrees, restart the mutation generation workflow with the updated field list
+
+## Retry and Escalation
+
+- **Maximum 2 test attempts** per generated query
+- If targeted resolution fails after 2 attempts, ask the user for additional details and **restart the entire workflow from Step 1 (Acquire Schema)** to re-validate entity and field information
diff --git a/skills/using-webapplication-salesforce-data/references/read-query-generation.md b/skills/using-webapplication-salesforce-data/references/read-query-generation.md
new file mode 100644
index 0000000..5ba0aad
--- /dev/null
+++ b/skills/using-webapplication-salesforce-data/references/read-query-generation.md
@@ -0,0 +1,307 @@
+# Read Query Generation
+
+## Generation Rules
+
+1. **No proliferation** — Only generate for explicitly requested fields, nothing else. Do NOT add fields the user did not ask for.
+2. **Unique query** — Leverage child relationships to query entities in one single query
+3. **Navigate entities** — Always use `relationshipName` to access reference fields and child entities. Exception: if `relationshipName` is null, return the `Id` itself
+4. **Leverage fragments** — Generate one fragment per possible type on polymorphic fields (fields with `dataType="REFERENCE"` and more than one entry in `referenceToInfos`)
+5. **Type consistency** — Variables used as query arguments and their related fields must share the same GraphQL type. Verify types against the schema search script output — do not assume types
+6. **Type enforcement** — Use field type information from introspection and the GraphQL schema to generate correct field access
+7. **Field name validation** — Every field name in the generated query **MUST** match a field confirmed via the schema search script. Do NOT guess or assume field names exist
+8. **@optional for FLS** — Apply `@optional` on all Salesforce record fields when possible (see [Field-Level Security and @optional](#field-level-security-and-optional)). This lets the query succeed when the user lacks field-level access; the server omits inaccessible fields instead of failing
+9. **Consuming code defense** — When generating or modifying code that consumes read query results, defend against missing fields (see [Field-Level Security and @optional](#field-level-security-and-optional)). Use optional chaining (`?.`), nullish coalescing (`??`), and null/undefined checks — never assume optional fields are present
+10. **Semi and anti joins** — Use the semi-join or anti-join templates to filter an entity with conditions on child entities
+11. **Explicit pagination** — Always include `first:` in every query to control page size (see [Pagination](#pagination)). Default is 10 if omitted.
+12. **Respect execution limits** — Stay within SOQL-derived limits: max 10 subqueries per request, max 5 child-to-parent relationship levels, max 1 parent-to-child level (no grandchildren), max 55 child-to-parent relationships, max 20 parent-to-child relationships per query
+13. **Compound fields** — When filtering, ordering, or aggregating, use constituent fields (e.g., `BillingCity`, `BillingCountry`) not the compound wrapper (`BillingAddress`). The compound wrapper is only for selection.
+14. **`_Record` suffix awareness** — Objects added to UI API in v60+ may use a `_Record` suffix for their type name (e.g., `FeedItem_Record` instead of `FeedItem`). Always verify type names via schema lookup — do not assume type name equals sObject API name.
+15. **Query generation** — Use the read query template below
+
+## Field-Level Security and @optional
+
+Field-level security (FLS) restricts which fields different users can see. Use the `@optional` directive on Salesforce record fields when possible. The server omits the field when the user lacks access, allowing the query to succeed instead of failing. Available in API v65.0+.
+
+Apply `@optional` to:
+- Scalar fields and value-type fields (e.g. `Name { value }`)
+- Parent relationships
+- Child relationships
+
+**Consuming code must defend against missing fields.** When a field is omitted due to FLS, it will be `undefined` (or absent) in the response. Use optional chaining (`?.`), nullish coalescing (`??`), and explicit null/undefined checks when reading query results. Never assume an optional field is present.
+
+```ts
+// Defend against missing fields
+const name = node.Name?.value ?? '';
+const relatedName = node.RelationshipName?.Name?.value ?? 'N/A';
+
+// Unsafe — will throw if field omitted due to FLS
+const name = node.Name.value;
+```
+
+## Pagination
+
+Salesforce GraphQL uses Relay Cursor Connections with **forward-only pagination**. There is no backward pagination (`last`/`before` are not supported).
+
+### Core Rules
+
+- **Always specify `first:`** — If omitted, the server defaults to 10 records. Be explicit.
+- **Forward-only** — Use `first` and `after` only. Do **not** use `last` or `before` — they are unsupported and will fail.
+- **Maximum without upperBound** — Standard pagination allows up to 4,000 total records across pages.
+- **Use `pageInfo`** — Select `pageInfo { hasNextPage endCursor }` for any query that may need pagination.
+
+### UpperBound Pagination (v59+)
+
+When you need more than 200 records per page or more than 4,000 total records, switch to upperBound mode:
+
+- **`first` must be 200–2000** when `upperBound` is set. Values below 200 are invalid.
+- **`upperBound`** declares the estimated total record count and enables extended pagination.
+
+```graphql
+# Standard pagination
+Account(first: 50, after: $cursor) {
+ edges { node { Id Name @optional { value } } }
+ pageInfo { hasNextPage endCursor }
+}
+
+# UpperBound pagination for large result sets
+Account(first: 2000, after: $cursor, upperBound: 10000) {
+ edges { node { Id Name @optional { value } } }
+ pageInfo { hasNextPage endCursor }
+}
+```
+
+## Ordering
+
+Use the `orderBy:` argument with generated `