@salesforce/afv-skills 1.34.0 → 1.36.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/package.json +1 -1
  2. package/skills/automation-sandbox-post-copy-config-generate/SKILL.md +239 -0
  3. package/skills/automation-sandbox-post-copy-config-generate/assets/config_template.json +21 -0
  4. package/skills/automation-sandbox-post-copy-config-generate/assets/json_schema.json +90 -0
  5. package/skills/automation-sandbox-post-copy-config-generate/examples/sample_sop_excerpt.md +31 -0
  6. package/skills/automation-sandbox-post-copy-config-generate/examples/sample_sop_to_config.json +50 -0
  7. package/skills/automation-sandbox-post-copy-config-generate/references/configuration_catalog.md +76 -0
  8. package/skills/automation-sandbox-post-copy-config-generate/references/sop_parsing_patterns.md +157 -0
  9. package/skills/automation-sandbox-post-copy-config-generate/references/source_format_handling.md +230 -0
  10. package/skills/dx-apexguru-scan/SKILL.md +403 -0
  11. package/skills/dx-apexguru-scan/examples/README.md +54 -0
  12. package/skills/dx-apexguru-scan/examples/sample-decoded-summary.json +176 -0
  13. package/skills/dx-apexguru-scan/examples/sample-full-no-runtime-response.json +26 -0
  14. package/skills/dx-apexguru-scan/examples/sample-succeeded-response.json +15 -0
  15. package/skills/dx-apexguru-scan/references/api-reference.md +81 -0
  16. package/skills/dx-apexguru-scan/references/authentication.md +134 -0
  17. package/skills/dx-apexguru-scan/references/error-handling.md +56 -0
  18. package/skills/dx-apexguru-scan/references/violation-catalog.md +28 -0
  19. package/skills/dx-apexguru-scan/scripts/build-zip.sh +87 -0
  20. package/skills/dx-apexguru-scan/scripts/decode-report.js +389 -0
  21. package/skills/dx-apexguru-scan/scripts/resolve-token.sh +151 -0
  22. package/skills/dx-apexguru-scan/scripts/run-scan.sh +153 -0
  23. package/skills/dx-apexguru-scan/scripts/scan.sh +96 -0
  24. package/skills/dx-apexguru-scan/scripts/validate-token.js +121 -0
  25. package/skills/dx-app-analytics-query/SKILL.md +2 -0
  26. package/skills/dx-devops-pipeline-manage/SKILL.md +263 -0
  27. package/skills/dx-devops-pipeline-manage/examples/common-workflows.md +177 -0
  28. package/skills/dx-devops-pipeline-manage/references/cli-commands.md +298 -0
  29. package/skills/dx-devops-pipeline-manage/references/parsing-patterns.md +134 -0
  30. package/skills/dx-devops-pipeline-manage/scripts/check-activation-ready.sh +34 -0
  31. package/skills/dx-devops-pipeline-manage/scripts/validate-org-type.sh +17 -0
  32. package/skills/dx-devops-pipeline-manage/scripts/verify-operation.sh +82 -0
  33. package/skills/dx-devops-promote/SKILL.md +214 -0
  34. package/skills/dx-devops-promote/examples/promotion-workflows.md +212 -0
  35. package/skills/dx-devops-promote/references/cli-commands.md +303 -0
  36. package/skills/experience-lwc-base-components-integrate/SKILL.md +176 -0
  37. package/skills/experience-lwc-base-components-integrate/references/lbc-expert-guidance.md +127 -0
  38. package/skills/experience-lwc-base-components-integrate/references/lightning-component-index.md +179 -0
  39. package/skills/experience-lwc-base-components-integrate/references/lightning-components.md +5429 -0
  40. package/skills/experience-lwc-base-components-integrate/scripts/extract-component-docs.sh +61 -0
  41. package/skills/experience-lwc-rtl-validate/SKILL.md +149 -0
  42. package/skills/experience-lwc-rtl-validate/references/rtl-expert.md +892 -0
  43. package/skills/experience-lwc-rtl-validate/scripts/scan-rtl-css.sh +206 -0
  44. package/skills/experience-lwc-typescript-migrate/SKILL.md +207 -0
  45. package/skills/experience-lwc-typescript-migrate/assets/dts-template.ts +15 -0
  46. package/skills/experience-lwc-typescript-migrate/assets/type-patterns.ts +44 -0
  47. package/skills/experience-lwc-typescript-migrate/scripts/find-consumers.sh +128 -0
  48. package/skills/experience-ui-bundle-localize/SKILL.md +323 -0
  49. package/skills/experience-ui-bundle-localize/references/gotchas.md +249 -0
  50. package/skills/experience-ui-bundle-localize/references/i18n-setup.md +169 -0
  51. package/skills/experience-ui-bundle-localize/references/interpolation.md +311 -0
  52. package/skills/experience-ui-bundle-localize/references/label-xml.md +282 -0
  53. package/skills/experience-ui-bundle-localize/references/verifying.md +219 -0
  54. package/skills/experience-ui-bundle-localize/scripts/check-i18n-wired.sh +195 -0
  55. package/skills/experience-ui-bundle-localize/scripts/check-manifest-registered.sh +100 -0
  56. package/skills/experience-ui-bundle-localize/scripts/check-org-api-version.sh +40 -0
  57. package/skills/experience-ui-bundle-localize/scripts/detect-bundle-type.sh +57 -0
  58. package/skills/experience-ui-bundle-site-generate/SKILL.md +3 -0
  59. package/skills/mobile-apps-create/SKILL.md +1 -3
  60. package/skills/platform-custom-lightning-type-generate/SKILL.md +3 -0
  61. package/skills/platform-custom-lightning-type-generate/assets/primitive-types-and-constraints.md +1 -1
  62. package/skills/platform-mcp-tool-widget-coordinate/SKILL.md +250 -0
  63. package/skills/platform-mcp-tool-widget-coordinate/examples/action-name-source-prompt.md +74 -0
  64. package/skills/platform-mcp-tool-widget-coordinate/examples/apex-invocable-source-prompt.md +90 -0
  65. package/skills/platform-mcp-tool-widget-coordinate/examples/nested-object-source-prompt.md +191 -0
  66. package/skills/platform-mcp-tool-widget-coordinate/examples/pasted-tool-output-prompt.md +85 -0
  67. package/skills/platform-mcp-tool-widget-coordinate/references/build-plan-format.md +74 -0
  68. package/skills/platform-mcp-tool-widget-coordinate/references/mcp-tool-output-discovery.md +184 -0
  69. package/skills/platform-mcp-tool-widget-coordinate/references/two-clt-modeling.md +128 -0
  70. package/skills/platform-mcp-tool-widget-coordinate/references/validation-gates.md +181 -0
@@ -0,0 +1,250 @@
1
+ ---
2
+ name: platform-mcp-tool-widget-coordinate
3
+ description: "Orchestrate object-based Lightning Type + HXL widget generation to render the output of a custom MCP server tool backed by an Apex Invocable Action. TRIGGER only when the prompt EXPLICITLY involves rendering an MCP tool result: user says 'MCP server', 'MCP tool', 'custom MCP server', references a tool 'output schema' / 'tool output' / 'outputValues' envelope, names an 'invocable action' backing an MCP tool, or asks to build a widget or rich UI rendition for the output of an Apex-invocable-backed MCP tool. DO NOT TRIGGER when: customizing an Apex-backed agent action output (use platform-lightning-type-widget-coordinate), authoring only a Custom Lightning Type (use platform-custom-lightning-type-generate), authoring only an Apex class (use platform-apex-generate), or building a standalone widget with no Lightning Type or MCP tool involved (use platform-widget-generate)."
4
+ metadata:
5
+ version: "1.0"
6
+ minApiVersion: "68.0"
7
+ relatedSkills:
8
+ - "platform-apex-generate"
9
+ - "platform-custom-lightning-type-generate"
10
+ - "platform-lightning-type-widget-coordinate"
11
+ - "platform-widget-generate"
12
+ cliTools:
13
+ - tool: ["jq"]
14
+ semver: ">=1.6.0"
15
+ - tool: ["sf"]
16
+ semver: ">=2.0.0"
17
+ ---
18
+
19
+ # Rendering a Custom MCP Tool Output With a Widget
20
+
21
+ Coordinate **two object-based Custom Lightning Types (CLTs)** and an **HXL widget** to render the output of a custom MCP server tool whose implementation is an Apex `@InvocableMethod`. This skill never authors content directly — it loads and invokes leaf skills in dependency order, gates progress on user approval, and runs validation gates before reporting completion.
22
+
23
+ ## Scope
24
+
25
+ Custom MCP server tools backed by an Apex Invocable Action only. The MCP tool returns the platform's **invocable-action result envelope** — an object with `actionName`, `isSuccess`, and an `outputValues` node that carries the tool's real payload. To render this envelope with a widget, model it as **two object-based CLTs** (`lightning__objectType`) of equal standing — the only reason there are two is that one must reference the other by name (a CLT cannot reference itself), so they need distinct deployed names. Name and describe each by what it actually models — never by an invented role-label pair like "Payload CLT"/"Envelope CLT" or "Inner CLT"/"Outer CLT":
26
+
27
+ - The CLT that mimics the tool-result envelope, named `<toolApiName>`. Its `outputValues` property is typed to the other CLT via `c__<responseCLT>`.
28
+ - The CLT that is the exact shape of the Invocable Action's **response** (`@InvocableVariable` fields on the `@InvocableMethod` response class), named `<toolApiName>Response` — "Response" here is not an invented role word, it's the same word the Apex source already uses for that class (e.g. `GetAccountSummaryResponse`).
29
+
30
+ The widget grounds on the **response fields** (flat), and the **default `renderer.json` in the envelope CLT** bridges the envelope nesting to the flat widget via `{!$attrs.outputValues.<field>}`.
31
+
32
+ **Out of scope, route elsewhere:**
33
+
34
+ - Customizing an **Apex-backed agent action** output (Apex-backed CLT `@apexClassType/...`, single CLT, surface-specific renderer) → `platform-lightning-type-widget-coordinate`.
35
+ - A standalone widget with no MCP tool / Lightning Type → `platform-widget-generate`.
36
+ - Authoring only a CLT or only an Apex class → `platform-custom-lightning-type-generate` / `platform-apex-generate`.
37
+
38
+ > **Beta cardinality:** the invocable-action result is a bulk array (`content[]`). For the beta release this skill models and renders a **single response** — the first element of `content[]`. The CLT envelope models one result object, not the `content[]` wrapper.
39
+
40
+ ---
41
+
42
+ ## How this differs from `platform-lightning-type-widget-coordinate`
43
+
44
+ | Dimension | agent-action flow (`...lightning-type-widget-coordinate`) | this MCP-tool flow |
45
+ |---|---|---|
46
+ | CLT kind | Apex-backed (`@apexClassType/...`) | Object-based (`lightning__objectType`) |
47
+ | Number of CLTs | one | **two** (envelope + response) |
48
+ | Field source | `@AuraEnabled` | **`@InvocableVariable`** on the response class |
49
+ | Renderer location | `lightningTypes/<T>/lightningDesktopGenAi/renderer.json` (surface-specific) | `lightningTypes/<toolCLT>/renderer.json` (**default, parallel to `schema.json`**) |
50
+ | Renderer binding | flat `{!$attrs.<field>}` | **nested `{!$attrs.outputValues.<field>}`** |
51
+
52
+ ---
53
+
54
+ ## Phase Graph
55
+
56
+ | Phase | Purpose | Output |
57
+ |---|---|---|
58
+ | 1 — Input selection | Determine the payload source: an **invocable action API name** (preferred), an Apex Invocable class, or a pasted tool-output JSON sample. | `source` (`action` \| `apex` \| `sample`), tool API name |
59
+ | 2 — Payload discovery | Describe the invocable action via the Actions REST API and read its typed `outputs` (or parse the response class from source, or `outputValues` from the sample). | `payloadFields` (name + `lightning:type`) |
60
+ | 3 — Build plan | Print the plan in full; proceed unless the next reply explicitly pushes back. | printed plan |
61
+ | 4 — Generation | Load and invoke leaf skills: response CLT → envelope CLT → widget → inline default renderer in the envelope CLT. | files written |
62
+ | 5 — Validation | Run hard gates (block) and warn gates (advisory). | gate report |
63
+ | 6 — Summary | Files, validations, deploy order, preview readiness. | summary |
64
+
65
+ **Per-phase pattern:** load the skill fresh → execute its workflow → verify outputs → checkpoint before the next phase. Even if you remember a leaf skill's content, skills evolve — always load fresh.
66
+
67
+ ---
68
+
69
+ ## Phase 1 — Input selection
70
+
71
+ Determine where the payload shape comes from. Prefer the sources top-to-bottom:
72
+
73
+ | Source | Trigger | Phase 2 action |
74
+ |---|---|---|
75
+ | `action` | Prompt gives an **invocable action API name** — directly, or via an Apex class name that resolves to one — AND an authenticated org is available. **Preferred.** | Describe the action via the Actions REST API and read its typed `outputs`. |
76
+ | `sample` | No reachable org (or the describe 404s), but a pasted tool-output JSON sample is available. | Parse the `outputValues` object from the sample. |
77
+ | `apex` | Only the Apex **class** is available (no action name resolvable, no reachable org, no sample) — fallback only, may be stale relative to what's deployed. | Resolve the response class and enumerate `@InvocableVariable` fields. |
78
+
79
+ Capture the **tool API name** (used to name all artifacts — see the naming convention below).
80
+
81
+ **Source priority:** live/authoritative schema sources beat parsing a local class, which beats a pasted example. In order:
82
+ 1. **`action`** if an action API name and an authenticated org are available. The Actions REST API describe is the same schema the platform itself exposes, so it needs no request/helper filtering and gives real field types.
83
+ 2. **`sample`** if a runtime JSON sample is pasted (runtime response — explicit and current).
84
+ 3. **`apex`** if an Apex class exists locally AND none of the above apply (fallback only — may be stale relative to what's actually deployed behind the action).
85
+
86
+ If none are available, STOP and ask the user for an action name, a class, a sample, or a schema.
87
+
88
+ ---
89
+
90
+ ## Phase 2 — Payload discovery
91
+
92
+ FIRST Read `references/mcp-tool-output-discovery.md` (REQUIRED — do NOT run Phase 2 from this summary alone), then execute the procedure for the chosen source. Reminders:
93
+
94
+ **For `action` (preferred):** describe the action with the Actions REST API and read its `outputs`:
95
+
96
+ - Resolve the action API name (for Apex actions this is the **class name** declaring `@InvocableMethod`, not the method label).
97
+ - `sf api request rest '/services/data/v<APIVER>/actions/custom/apex/<ActionApiName>' -o <org>`.
98
+ - Use the `outputs` array only (each entry `{ name, label, type, maxOccurs }`). **Ignore `inputs`** — that is the tool input (request wrapper). A field with `maxOccurs > 1` is a list — surface it in the plan (beta renders a single response).
99
+ - Map the Actions API `type` → CLT `lightning:type` (see the discovery reference's table; `STRING`/`ID`/`REFERENCE`/… → `lightning__textType`, `INTEGER`/`LONG` → `lightning__integerType`, `DOUBLE`/`DECIMAL`/`CURRENCY`/`PERCENT` → `lightning__numberType`, `BOOLEAN` → `lightning__booleanType`, `DATE`/`DATETIME` → date types). Match case-insensitively.
100
+ - An entry with **`"type": null` and an `"apexClass": "<OuterClass>$<InnerClass>"` key** instead of a primitive `type` is a nested-object field, not a describe gap — see "Nested-object payload fields" below. The describe never exposes that class's own leaf fields, so retrieving/reading the named Apex class to enumerate them is the expected next step, not a fallback away from `action`.
101
+ - If the describe 404s, fall back to `sample` (a pasted tool-output JSON) if one is available, else `apex` (parse the class from source, if locally available).
102
+
103
+ **For `apex` (fallback):**
104
+
105
+ - Locate the class (`<pkgDir>/classes/<ClassName>.cls`; retrieve `ApexClass:<ClassName>` if absent).
106
+ - Identify the **response class** — the element type of the `@InvocableMethod` return `List<...>` (e.g. `List<GetAccountSummaryResponse>` → `GetAccountSummaryResponse`).
107
+ - Enumerate its `@InvocableVariable` fields. **Exclude** the request class (the `@InvocableMethod` parameter type) and any `private` helper classes — those are not part of the tool's externally visible output schema.
108
+ - Map Apex → CLT `lightning:type`: `String`/`Id` → `lightning__textType`, `Integer` → `lightning__integerType`, `Decimal`/`Double` → `lightning__numberType`, `Boolean` → `lightning__booleanType`, `Date` → `lightning__dateType`, `Datetime` → `lightning__dateTimeType`.
109
+ - **A field whose type is itself an Apex class** (e.g. `flightInfo : SearchFlightsAction.Flight`) is a second, additive case alongside the flat-primitive mapping above — see "Nested-object payload fields" below. This applies to every source (`action`, `apex`, `sample`), not just `apex`.
110
+
111
+ **For `sample` (fallback):** parse the `outputValues` object; infer each field's `lightning:type` from its JSON value (string → `lightning__textType`, integer → `lightning__integerType`, fractional number → `lightning__numberType`, boolean → `lightning__booleanType`).
112
+
113
+ **Nested-object payload fields (applies to every source above):** when a response/output field's type is not a primitive but another Apex class (object), do NOT model it as `{"type":"object"}` or an inlined `lightning__objectType` — both produce an opaque, unrenderable blob and neither deploys. Instead:
114
+ - Type the response CLT property as `"@apexClassType/<ns>__<OuterClass>$<InnerClass>"` (e.g. `"@apexClassType/c__SearchFlightsAction$Flight"`), matching the Apex-backed-CLT convention `platform-custom-lightning-type-generate` already documents.
115
+ - Enumerate the referenced Apex class's own `@InvocableVariable`/public fields as the leaf set; the widget schema flattens to those leaves (never the object field itself).
116
+ - The renderer binds one level deeper: `{!$attrs.outputValues.<objectField>.<leaf>}` (e.g. `{!$attrs.outputValues.flightInfo.flightId}`), not `{!$attrs.outputValues.<objectField>}`.
117
+ - A field typed `List<ApexClass>` is out of scope for the beta single-response flow — surface it in the build plan like a `maxOccurs > 1` scalar, do not emit a schema for it.
118
+ - See `references/two-clt-modeling.md` ("Nested-object payload fields") and `examples/nested-object-source-prompt.md` for the full walkthrough.
119
+
120
+ Capture `payloadFields` — the ordered list of `{ name, title, lightning:type }` that defines the response CLT and the widget schema. Record which source produced it in the build plan.
121
+
122
+ > Staleness: do NOT maintain a cross-session cache. Read the local project fresh and re-retrieve from the org per session.
123
+
124
+ ---
125
+
126
+ ## Phase 3 — Build plan + approval gate
127
+
128
+ Print a build plan using the template in `references/build-plan-format.md`. The plan must list:
129
+
130
+ - A one-line developer-facing summary (the `PLAN:` line).
131
+ - The tool API name and the response class FQN (or "from pasted sample").
132
+ - The two CLT names (envelope + payload) and the widget name, with absolute paths.
133
+ - The envelope fields the envelope CLT will carry, and the response fields the response CLT + widget will carry.
134
+ - `Properties omitted:` — any response fields intentionally dropped, with rationale. **`actionName` and `isSuccess` are envelope fields and never appear here or on the widget** — they belong to the envelope CLT only. Any payload-side operational/status field (`isSuccess`, `errorMessage`, `status`, `message`, and similar) that the response happens to carry is omitted from the widget by default and MUST be listed here with a one-line rationale — the widget renders `outputValues` data fields only.
135
+ - The validations that will run after generation.
136
+
137
+ **Print the plan in full, then proceed unless the user's next reply explicitly pushes back.** Explicit pushback = `no`, `stop`, `wait`, `change X`, `use Y instead`, or an equivalent rejection / revision request. Explicit approval is welcome but NOT required — silence, an unrelated follow-up, or the natural continuation of a single-turn eval all count as implicit approval. The invariant is the plan being visible in the transcript. If pushback arrives, revise and re-print before moving on.
138
+
139
+ ---
140
+
141
+ ## Phase 4 — Generation
142
+
143
+ Load and invoke leaf skills in this order. For each: load the skill, execute its workflow against the Phase 3 spec, verify the outputs, checkpoint before the next.
144
+
145
+ 1. **Response CLT** — load `platform-custom-lightning-type-generate`. Author an object-based CLT `<responseCLT>` (convention: `<toolApiName>Response`) whose `properties` are the `payloadFields` from Phase 2 (1:1 with the response `@InvocableVariable` fields). Root is `lightning__objectType`, with root-level `"lightning:tags": ["mcp"]`.
146
+
147
+ 2. **Envelope CLT** — load `platform-custom-lightning-type-generate`. Author an object-based CLT `<toolCLT>` (convention: `<toolApiName>`, the envelope), also with root-level `"lightning:tags": ["mcp"]`, and:
148
+ - `actionName` → `lightning__textType`
149
+ - `isSuccess` → `lightning__booleanType`
150
+ - `outputValues` → **`c__<responseCLT>`** (the referenced-CLT pattern; the response CLT must be deployed before the envelope CLT)
151
+ - Add `message` / other envelope scalars only if the widget needs to render them.
152
+
153
+ 3. **Widget** — load `platform-widget-generate`. Author a **flat** widget whose `schema.json` properties are the `payloadFields` (name + primitive type) — a standalone widget contract, not derived from or coupled to any Lightning Type. It renders **only `outputValues` data fields**: never `actionName`/`isSuccess` (envelope-only), and never a response-side operational/status field declared in `Properties omitted:`. The widget body binds each field via `{!$attrs.<field>}` — the widget is envelope-agnostic and never references `outputValues` itself.
154
+
155
+ 4. **Default renderer (authored inline in the ENVELOPE CLT — never optional).** FIRST Read `platform-custom-lightning-type-generate/references/widget-rendition.md` (REQUIRED — do NOT author from memory or copy an existing sample, which may use a deprecated shape). Then author `<pkgDir>/lightningTypes/<toolCLT>/renderer.json` — the **default renderer, at the bundle root, parallel to `schema.json`** (NOT under `lightningDesktopGenAi/`). Shape:
156
+
157
+ ```json
158
+ {
159
+ "renderer": {
160
+ "componentOverrides": {
161
+ "$": {
162
+ "definition": "@widget/c/<widgetName>",
163
+ "attributes": {
164
+ "<payloadField>": "{!$attrs.outputValues.<payloadField>}"
165
+ }
166
+ }
167
+ }
168
+ }
169
+ }
170
+ ```
171
+
172
+ The renderer maps **every widget schema property** to the matching payload field nested under the envelope's `outputValues` node via `{!$attrs.outputValues.<payloadField>}`. This nested binding is the crux of this flow — it bridges the envelope CLT to the flat widget. Do **NOT** duplicate the widget body inside `renderer.json`.
173
+
174
+ **Existing-renderer handling:** if `renderer.json` already exists at the target path, read it first. If it references the same widget with the same bindings, leave it. If it references a different widget or a custom-LWC root override (`c/<component>`), STOP and surface the conflict before overwriting.
175
+
176
+ ---
177
+
178
+ ## Phase 5 — Validation gates
179
+
180
+ Read `references/validation-gates.md` and **run every gate**. Widget-bundle-internal checks (schema parse, root keys, leaf types, `{!$attrs.X}` resolution, `.uiwidget-meta.xml` well-formedness) are owned by `platform-widget-generate` and run in its own self-validation.
181
+
182
+ **Hard — block on failure:**
183
+
184
+ 1. `clt-reference-integrity` — the envelope CLT's `outputValues` property has `lightning:type === "c__<responseCLT>"`, the response CLT exists at `<pkgDir>/lightningTypes/<responseCLT>/schema.json`, both parse as JSON, and neither carries `$schema` or (nested) `items`.
185
+ 2. `renderer-wires-widget` — `<pkgDir>/lightningTypes/<toolCLT>/renderer.json` exists (at the bundle root, **not** `lightningDesktopGenAi/`), parses, wires the widget via `componentOverrides["$"].definition === "@widget/c/<widgetName>"`, and binds every widget schema property as **`{!$attrs.outputValues.<property>}`** (nested path). Bidirectional: missing or extra bindings both fail.
186
+
187
+ **Warn — advisory:**
188
+
189
+ 1. `field-trace` — RUN the trace in `references/validation-gates.md`: grep `@InvocableVariable` from the **response** class, `jq` the widget schema property keys, print both lists, classify INVENTED vs OMITTED. Invented widget fields fail; silent omissions (a response field absent from the widget AND absent from the Phase 3 `Properties omitted:` plan) warn.
190
+
191
+ Report each gate result by name in Phase 6 (`pass`, `fail (<reason>)`, `warn (<reason>)`, `not run`). Do **not** summarize as "all passed". This skill produces metadata only — it does not deploy; deployment is the caller's responsibility.
192
+
193
+ ---
194
+
195
+ ## Phase 6 — Summary
196
+
197
+ ```text
198
+ MCP Tool Widget Build Complete: <widgetName>
199
+
200
+ FILES GENERATED:
201
+ Response CLT:
202
+ <pkgDir>/lightningTypes/<responseCLT>/schema.json
203
+ Envelope CLT:
204
+ <pkgDir>/lightningTypes/<toolCLT>/schema.json
205
+ <pkgDir>/lightningTypes/<toolCLT>/renderer.json # default renderer — wires the widget
206
+ Widget bundle:
207
+ <pkgDir>/uiWidgets/<widgetName>/<widgetName>.json
208
+ <pkgDir>/uiWidgets/<widgetName>/schema.json
209
+ <pkgDir>/uiWidgets/<widgetName>/<widgetName>.uiwidget-meta.xml
210
+
211
+ VALIDATIONS:
212
+ widget self-validation (platform-widget-generate gates): <pass | fail — see sub-skill report>
213
+ clt-reference-integrity (envelope.outputValues → c__<responseCLT>): <pass | fail (<reason>)>
214
+ renderer-wires-widget (nested {!$attrs.outputValues.X} bindings): <pass | fail (<reason>)>
215
+ field-trace (INVENTED + OMITTED lists printed): <pass | warn (<reason>) | fail (invented: <list>)>
216
+ ```
217
+
218
+ ---
219
+
220
+ ## Hard Rules (always apply)
221
+
222
+ 1. **Plan-first, then proceed.** Print the full Phase 3 build plan before writing any file. Explicit rejection or a change request → stop and revise; otherwise continue. The invariant is the plan being visible in the transcript, not an interactive human approval — this holds in manual chat, agent-to-agent flows, and single-turn evals.
223
+ 2. **Two object-based CLTs, never one.** The envelope and the payload are separate CLTs. The envelope's `outputValues` is typed via `c__<responseCLT>`, never inlined as a nested `lightning__objectType`. Both CLTs carry root-level `"lightning:tags": ["mcp"]` (see `platform-custom-lightning-type-generate/assets/primitive-types-and-constraints.md`).
224
+ 3. **Renderer lives in the ENVELOPE CLT, at the bundle root.** `lightningTypes/<toolCLT>/renderer.json` — the default renderer, parallel to `schema.json`. Never `lightningDesktopGenAi/renderer.json` (that is the agent-action flow's surface-specific path), never in the response CLT.
225
+ 4. **Renderer bindings are nested.** Every widget attribute maps to `{!$attrs.outputValues.<field>}`, not `{!$attrs.<field>}`. The widget schema stays flat; the renderer does the bridging.
226
+ 5. **Widget grounds on the payload, not the envelope, and renders `outputValues` data fields only.** The widget schema properties are the payload fields. The widget never references `actionName` or `isSuccess` — those are envelope-only. A payload field that is itself operational/status (`isSuccess`, `errorMessage`, `status`, `message`, and similar) is omitted from the widget by default and declared in `Properties omitted:`.
227
+ 6. **Field source is `@InvocableVariable` on the response class.** Enumerate the response class only. Exclude the request class and private helper classes.
228
+ 7. **No invented fields, no silent omissions.** The widget schema (and the response CLT) must be a subset of the response `@InvocableVariable` fields. Omission requires the field to appear in the Phase 3 `Properties omitted:` section with an approved rationale. `field-trace` prints both lists.
229
+ 8. **Single response for beta.** Model one result object, not the `content[]` bulk wrapper.
230
+ 9. **Always load the leaf skill** before generation. Do not author from memory.
231
+ 10. **Run gates, do not describe them.** Reporting `pass` without executing a gate is a hard violation; report `not run` instead.
232
+ 11. **No shell metacharacters that trigger the Vibes safe-shell filter.** In every `Bash` tool call emitted by this orchestrator and by any leaf skill it invokes, do NOT use command substitution (`$(…)` or backticks), process substitution (`<(…)`, `>(…)`), brace expansion (`{a,b,c}` or `{1..N}`), or `eval` / `exec`. These force manual approval even under Bypass mode and stall the eval. Run separate commands (`mkdir -p a && mkdir -p b`), print each intermediate value with its own command and reason about the result, and use plain shell variables (`X=literal`) or here-strings when a value must be reused.
233
+ 12. **Resolve `action` schema from the Actions REST API describe, never from raw HTTP to the MCP endpoint or from credential extraction.** Use `sf api request rest` against the org's Actions REST API (which uses the existing `sf` org auth). Never read `a4d_mcp_settings.json` or any MCP settings file, never extract an org access token, never `curl` an MCP server endpoint directly — that requires credentials the session doesn't have and targets a URL the runtime doesn't actually expose that way.
234
+ 13. **Never invoke an MCP tool to discover its output shape.** Describing the payload must never execute the underlying action. Resolve the schema via the Actions REST API describe of the backing action — never by calling the tool with sample/guessed input to observe a response. If no action name is resolvable, ask the user for a pasted `sample` instead of invoking anything.
235
+ 14. **A response field typed as another Apex class is never a bare `{"type":"object"}`.** Type it `@apexClassType/<ns>__<OuterClass>$<InnerClass>` in the response CLT, flatten to its leaf fields in the widget, and bind the renderer two levels deep (`{!$attrs.outputValues.<objectField>.<leaf>}`). This is additive to the flat-primitive case (Hard Rule 5), not a replacement for it — see `references/two-clt-modeling.md`.
236
+
237
+ ---
238
+
239
+ ## Reference File Index
240
+
241
+ | File | When to read |
242
+ |------|--------------|
243
+ | `references/mcp-tool-output-discovery.md` | Phase 2 — the three sources (`action` describe via Actions REST API; pasted `sample`; `apex` class parse), field enumeration, and type mapping. |
244
+ | `references/two-clt-modeling.md` | Phase 4 — how the envelope + response CLTs and the nested renderer binding fit together, with the naming convention and nested-object payload handling. |
245
+ | `references/build-plan-format.md` | Phase 3 — plan template the model fills before proceeding. |
246
+ | `references/validation-gates.md` | Phase 5 — full hard / warn gate table with RUN procedures. |
247
+ | `examples/action-name-source-prompt.md` | Phase 3 — a complete walkthrough starting from an invocable action API name (preferred source). |
248
+ | `examples/apex-invocable-source-prompt.md` | Phase 3 — a complete walkthrough starting from an Apex Invocable class (fallback). |
249
+ | `examples/pasted-tool-output-prompt.md` | Phase 3 — a complete walkthrough starting from a pasted tool-output sample (fallback). |
250
+ | `examples/nested-object-source-prompt.md` | Phase 3 — a complete walkthrough where a payload field is itself an Apex-class reference, not a primitive. |
@@ -0,0 +1,74 @@
1
+ # Example: Invocable action name source (preferred)
2
+
3
+ A complete walkthrough when the user gives only the **invocable action API name** and the action is deployed to a reachable org (`source = action`). No `.cls` parsing — the Actions REST API describes the typed outputs directly.
4
+
5
+ ## The prompt
6
+
7
+ > My custom MCP server has a GetAccountSummary tool backed by the `GetAccountSummaryTest` invocable action in my org. Build a widget that renders its output as a rich account card.
8
+
9
+ ## Phase 1 — Input selection
10
+
11
+ - Source: `action` (prompt gives an action API name; an authenticated org is available).
12
+ - Tool API name: `getAccountSummary` (camelCase of the tool / action label).
13
+ - Action API name: `GetAccountSummaryTest` (the Apex class declaring `@InvocableMethod`).
14
+
15
+ ## Phase 2 — Payload discovery
16
+
17
+ Read `references/mcp-tool-output-discovery.md`, then describe the action:
18
+
19
+ ```bash
20
+ sf api request rest '/services/data/v63.0/actions/custom/apex/GetAccountSummaryTest' -o myOrg
21
+ ```
22
+
23
+ Read the `outputs` array (ignore `inputs` — that is `accountId`, the tool input). Map each `type`:
24
+
25
+ | output `name` | Actions API `type` | CLT `lightning:type` |
26
+ |---|---|---|
27
+ | status | STRING | `lightning__textType` |
28
+ | message | STRING | `lightning__textType` |
29
+ | accountId | ID | `lightning__textType` |
30
+ | accountName | STRING | `lightning__textType` |
31
+ | accountDescription | STRING | `lightning__textType` |
32
+ | accountIndustry | STRING | `lightning__textType` |
33
+ | accountPhone | STRING | `lightning__textType` |
34
+ | accountWebsite | STRING | `lightning__textType` |
35
+ | contactCount | INTEGER | `lightning__integerType` |
36
+ | opportunityCount | INTEGER | `lightning__integerType` |
37
+ | largestOpportunityId | ID | `lightning__textType` |
38
+ | largestOpportunityName | STRING | `lightning__textType` |
39
+ | largestOpportunityAmount | DOUBLE | `lightning__numberType` |
40
+ | totalOpportunityAmount | DOUBLE | `lightning__numberType` |
41
+
42
+ `payloadFields` = the 14 rows above — identical to what the `apex` source would enumerate, but resolved from the live org without parsing source or filtering the request/helper classes.
43
+
44
+ > If any output had `maxOccurs > 1`, it would be a list — flag it in the plan (beta renders a single response).
45
+
46
+ ## Phase 3 — Build plan (abridged)
47
+
48
+ ```text
49
+ MCP Tool Widget Build Plan: getAccountSummaryWidget
50
+
51
+ PLAN: Render the GetAccountSummary MCP tool output as an account-summary card.
52
+
53
+ TOOL / SOURCE:
54
+ Tool API name: getAccountSummary
55
+ Payload source: action: Actions REST describe of GetAccountSummaryTest
56
+
57
+ LIGHTNING TYPES:
58
+ Response CLT: getAccountSummaryResponse
59
+ Envelope CLT: getAccountSummary
60
+ Renderer (default, bundle root): .../lightningTypes/getAccountSummary/renderer.json
61
+ Envelope: actionName (text), isSuccess (boolean), outputValues (c__getAccountSummaryResponse)
62
+
63
+ WIDGET: getAccountSummaryWidget
64
+ Renderer binding: each attribute → {!$attrs.outputValues.<field>}
65
+ Properties omitted: status, message
66
+
67
+ GENERATION ORDER: response CLT → widget → envelope CLT
68
+ ```
69
+
70
+ Proceed unless the next reply pushes back.
71
+
72
+ ## Phases 4–5
73
+
74
+ Generation and validation are identical to the `apex` walkthrough (`apex-invocable-source-prompt.md`), since both produce the same `payloadFields`. In `field-trace`, `INVOCABLE_FIELDS` comes from `jq -r '.outputs[].name'` on the describe rather than a `grep` of the `.cls`.
@@ -0,0 +1,90 @@
1
+ # Example: Apex Invocable source
2
+
3
+ A complete walkthrough of the flow when the payload source is an Apex `@InvocableMethod` class that already exists in the project (`source = apex`). This is the fallback source — when the action is deployed to a reachable org, prefer `action` (describe it by name via the Actions REST API; see `action-name-source-prompt.md`). Use `apex` when no org is reachable or the describe 404s.
4
+
5
+ ## The prompt
6
+
7
+ > I have an MCP server tool backed by the `GetAccountSummaryTest` Apex invocable action. Build a widget that renders its output so the account summary shows up as a rich card.
8
+
9
+ ## Phase 1 — Input selection
10
+
11
+ - Source: `apex` (the prompt names an Apex Invocable class).
12
+ - Tool API name: `getAccountSummary` (from the class / invocable label `Get Account Summary`).
13
+
14
+ ## Phase 2 — Payload discovery
15
+
16
+ Read `references/mcp-tool-output-discovery.md`, then locate `.../classes/GetAccountSummaryTest.cls`.
17
+
18
+ The invocable method:
19
+
20
+ ```apex
21
+ @InvocableMethod(label='Get Account Summary')
22
+ global static List<GetAccountSummaryResponse> getAccountSummary(
23
+ List<GetAccountSummaryRequest> requests
24
+ ) { ... }
25
+ ```
26
+
27
+ - Response class = `GetAccountSummaryResponse` (the `List<...>` element type) → **payload source**.
28
+ - Request class = `GetAccountSummaryRequest` → excluded (tool input).
29
+ - `private class OpportunityMetrics` → excluded (internal helper, not part of the invocable schema).
30
+
31
+ Enumerate `@InvocableVariable` fields on `GetAccountSummaryResponse` and map to CLT types:
32
+
33
+ | Field | Apex type | CLT `lightning:type` |
34
+ |---|---|---|
35
+ | status | String | `lightning__textType` |
36
+ | message | String | `lightning__textType` |
37
+ | accountId | Id | `lightning__textType` |
38
+ | accountName | String | `lightning__textType` |
39
+ | accountDescription | String | `lightning__textType` |
40
+ | accountIndustry | String | `lightning__textType` |
41
+ | accountPhone | String | `lightning__textType` |
42
+ | accountWebsite | String | `lightning__textType` |
43
+ | contactCount | Integer | `lightning__integerType` |
44
+ | opportunityCount | Integer | `lightning__integerType` |
45
+ | largestOpportunityId | Id | `lightning__textType` |
46
+ | largestOpportunityName | String | `lightning__textType` |
47
+ | largestOpportunityAmount | Decimal | `lightning__numberType` |
48
+ | totalOpportunityAmount | Decimal | `lightning__numberType` |
49
+
50
+ `payloadFields` = the 14 rows above.
51
+
52
+ ## Phase 3 — Build plan (abridged)
53
+
54
+ ```text
55
+ MCP Tool Widget Build Plan: getAccountSummaryWidget
56
+
57
+ PLAN: Render the GetAccountSummary MCP tool output as an account-summary card.
58
+
59
+ TOOL / SOURCE:
60
+ Tool API name: getAccountSummary
61
+ Payload source: Apex Invocable class GetAccountSummaryTest
62
+ Response class FQN: GetAccountSummaryTest.GetAccountSummaryResponse
63
+
64
+ LIGHTNING TYPES:
65
+ Response CLT: getAccountSummaryResponse (14 payload properties)
66
+ Envelope CLT: getAccountSummary
67
+ Renderer (default, bundle root): .../lightningTypes/getAccountSummary/renderer.json
68
+ Envelope: actionName (text), isSuccess (boolean), outputValues (c__getAccountSummaryResponse)
69
+
70
+ WIDGET: getAccountSummaryWidget
71
+ Renderer binding: each attribute → {!$attrs.outputValues.<field>}
72
+ Properties omitted: status, message (envelope-status text, not rendered on the card)
73
+
74
+ GENERATION ORDER: response CLT → widget → envelope CLT
75
+ ```
76
+
77
+ Proceed unless the next reply pushes back.
78
+
79
+ ## Phase 4 — Generation order
80
+
81
+ 1. Response CLT `getAccountSummaryResponse` — `lightning__objectType`, all 14 `payloadFields` (1:1 with the response class's `@InvocableVariable` fields — the response CLT always models the complete response, including `status`/`message`).
82
+ 2. Envelope CLT `getAccountSummary` — `actionName`, `isSuccess`, `outputValues` → `c__getAccountSummaryResponse`.
83
+ 3. Widget `getAccountSummaryWidget` — flat schema over the 12 rendered payload fields (14 minus the two `Properties omitted:` status fields); body binds `{!$attrs.accountName}` etc.
84
+ 4. Default renderer at `lightningTypes/getAccountSummary/renderer.json` — `definition: @widget/c/getAccountSummaryWidget`, each attribute `{!$attrs.outputValues.<field>}`.
85
+
86
+ ## Phase 5 — Validation
87
+
88
+ - `clt-reference-integrity`: envelope `outputValues` → `c__getAccountSummaryResponse`, response CLT exists, no `$schema`/`items` → **pass**.
89
+ - `renderer-wires-widget`: bundle-root renderer present, definition `@widget/c/getAccountSummaryWidget`, every widget prop bound as `{!$attrs.outputValues.<prop>}` → **pass**.
90
+ - `field-trace`: INVENTED empty; OMITTED = `status, message` (both in `Properties omitted:`) → **pass**.
@@ -0,0 +1,191 @@
1
+ # Example: Nested-object payload field (Apex-class-typed response field)
2
+
3
+ A complete walkthrough of the flow when one `@InvocableVariable` field on the response class is
4
+ itself **another Apex class**, not a primitive (`source = apex`, nested-object branch). This is the
5
+ second, additive payload shape alongside the flat-primitive shape in `apex-invocable-source-prompt.md`
6
+ — read `references/mcp-tool-output-discovery.md` ("Nested-object payload fields") and
7
+ `references/two-clt-modeling.md` ("Nested-object payload fields (a second, additive case)") first.
8
+
9
+ ## The prompt
10
+
11
+ > I have an MCP server tool backed by the `GetFlightDetailsAction` Apex invocable action. Build a
12
+ > widget that renders its output as a flight details card.
13
+
14
+ ## Phase 1 — Input selection
15
+
16
+ - Source: `apex` (the prompt names an Apex Invocable class; no org describe needed for this walkthrough).
17
+ - Tool API name: `getFlightDetails` (from the class name `GetFlightDetailsAction`, stripping the
18
+ trailing `Action` suffix and lower-camelCasing — see the naming convention in
19
+ `references/two-clt-modeling.md`).
20
+
21
+ ## Phase 2 — Payload discovery
22
+
23
+ Read `references/mcp-tool-output-discovery.md`, then locate `.../classes/GetFlightDetailsAction.cls`.
24
+
25
+ The invocable method:
26
+
27
+ ```apex
28
+ @InvocableMethod(label='Get Flight Details' description='Returns flight details based on input ID, Origin, and Destination')
29
+ public static List<FlightDetailsResponse> getFlightDetails(List<FlightDetailsRequest> requests) { ... }
30
+
31
+ public class FlightDetailsResponse {
32
+ @InvocableVariable(label='Flight Details')
33
+ public SearchFlightsAction.Flight flightInfo;
34
+ }
35
+ ```
36
+
37
+ - Response class = `FlightDetailsResponse` (the `List<...>` element type) → **payload source**.
38
+ - Request class = `FlightDetailsRequest` → excluded (tool input).
39
+ - The single `@InvocableVariable` field, `flightInfo`, is declared `SearchFlightsAction.Flight` —
40
+ **another Apex class**, not `String`/`Integer`/etc. This is the nested-object branch: it does not
41
+ go in the primitive Apex→CLT mapping table.
42
+
43
+ Enumerate the referenced class (`SearchFlightsAction.Flight`) for its own `@InvocableVariable`
44
+ leaf fields — these become the widget's flat properties:
45
+
46
+ ```apex
47
+ public class Flight {
48
+ @InvocableVariable public String flightId;
49
+ @InvocableVariable public String origin;
50
+ @InvocableVariable public String destination;
51
+ @InvocableVariable public String departureTime;
52
+ @InvocableVariable public String arrivalTime;
53
+ @InvocableVariable public Long price;
54
+ }
55
+ ```
56
+
57
+ | Leaf field | Apex type | CLT / widget `lightning:type` |
58
+ |---|---|---|
59
+ | flightId | String | `lightning__textType` |
60
+ | origin | String | `lightning__textType` |
61
+ | destination | String | `lightning__textType` |
62
+ | departureTime | String | `lightning__textType` |
63
+ | arrivalTime | String | `lightning__textType` |
64
+ | price | Long | `lightning__numberType` (widget uses `lightning__numberType` for all numerics; the response CLT would use `lightning__integerType` if this leaf were typed directly on the response CLT — but it isn't, since it's flattened through `@apexClassType`, see below) |
65
+
66
+ `payloadFields` (top-level) = 1 field: `flightInfo`, typed as an Apex class → nested-object branch.
67
+ `payloadFields` (flattened, for the widget) = the 6 leaf rows above.
68
+
69
+ ## Phase 3 — Build plan (abridged)
70
+
71
+ ```text
72
+ MCP Tool Widget Build Plan: getFlightDetailsWidget
73
+
74
+ PLAN: Render the GetFlightDetails MCP tool output as a flight-details card.
75
+
76
+ TOOL / SOURCE:
77
+ Tool API name: getFlightDetails
78
+ Payload source: Apex Invocable class GetFlightDetailsAction
79
+ Response class FQN: GetFlightDetailsAction.FlightDetailsResponse
80
+
81
+ LIGHTNING TYPES:
82
+ Response CLT: getFlightDetailsResponse
83
+ Properties: flightInfo → "@apexClassType/c__SearchFlightsAction$Flight" # nested-object field, NOT {"type":"object"}
84
+ Envelope CLT: getFlightDetails
85
+ Renderer (default, bundle root): .../lightningTypes/getFlightDetails/renderer.json
86
+ Envelope: actionName (text), isSuccess (boolean), outputValues (c__getFlightDetailsResponse)
87
+
88
+ WIDGET: getFlightDetailsWidget
89
+ Schema: flattened to flightInfo's 6 leaf fields (flightId, origin, destination, departureTime, arrivalTime, price)
90
+ Renderer binding: each attribute → {!$attrs.outputValues.flightInfo.<leaf>} # two levels deep, not one
91
+ Properties omitted: none
92
+
93
+ GENERATION ORDER: response CLT → widget → envelope CLT
94
+ ```
95
+
96
+ Proceed unless the next reply pushes back.
97
+
98
+ ## Phase 4 — Generation order
99
+
100
+ 1. **Response CLT** `getFlightDetailsResponse`:
101
+
102
+ ```json
103
+ {
104
+ "title": "Get Flight Details Response",
105
+ "description": "Response fields from the GetFlightDetailsAction invocable-action (FlightDetailsResponse)",
106
+ "type": "object",
107
+ "lightning:type": "lightning__objectType",
108
+ "lightning:tags": ["mcp"],
109
+ "unevaluatedProperties": false,
110
+ "properties": {
111
+ "flightInfo": {
112
+ "title": "Flight Info",
113
+ "lightning:type": "@apexClassType/c__SearchFlightsAction$Flight"
114
+ }
115
+ }
116
+ }
117
+ ```
118
+
119
+ 2. **Widget** `getFlightDetailsWidget` — flat schema over the 6 leaf fields (not the single
120
+ `flightInfo` field); body binds `{!$attrs.flightId}`, `{!$attrs.origin}`, etc.
121
+
122
+ 3. **Envelope CLT** `getFlightDetails`:
123
+
124
+ ```json
125
+ {
126
+ "title": "Get Flight Details",
127
+ "description": "Invocable-action result envelope for the GetFlightDetailsAction MCP tool",
128
+ "type": "object",
129
+ "lightning:type": "lightning__objectType",
130
+ "lightning:tags": ["mcp"],
131
+ "unevaluatedProperties": false,
132
+ "properties": {
133
+ "actionName": { "title": "Action Name", "lightning:type": "lightning__textType" },
134
+ "isSuccess": { "title": "Is Success", "lightning:type": "lightning__booleanType" },
135
+ "outputValues": { "title": "Output Values", "lightning:type": "c__getFlightDetailsResponse" }
136
+ }
137
+ }
138
+ ```
139
+
140
+ 4. **Default renderer** at `lightningTypes/getFlightDetails/renderer.json` —
141
+ `definition: @widget/c/getFlightDetailsWidget`, each attribute bound two levels deep through the
142
+ nested object:
143
+
144
+ ```json
145
+ {
146
+ "renderer": {
147
+ "componentOverrides": {
148
+ "$": {
149
+ "definition": "@widget/c/getFlightDetailsWidget",
150
+ "attributes": {
151
+ "flightId": "{!$attrs.outputValues.flightInfo.flightId}",
152
+ "origin": "{!$attrs.outputValues.flightInfo.origin}",
153
+ "destination": "{!$attrs.outputValues.flightInfo.destination}",
154
+ "departureTime": "{!$attrs.outputValues.flightInfo.departureTime}",
155
+ "arrivalTime": "{!$attrs.outputValues.flightInfo.arrivalTime}",
156
+ "price": "{!$attrs.outputValues.flightInfo.price}"
157
+ }
158
+ }
159
+ }
160
+ }
161
+ }
162
+ ```
163
+
164
+ ## Phase 5 — Validation
165
+
166
+ - `clt-reference-integrity`: envelope `outputValues` → `c__getFlightDetailsResponse`,
167
+ response CLT exists, `flightInfo` uses `@apexClassType/c__SearchFlightsAction$Flight` (not a
168
+ bare `{"type":"object"}` and not an inlined `lightning__objectType`), no `$schema`/`items` →
169
+ **pass**.
170
+ - `renderer-wires-widget`: bundle-root renderer present, definition `@widget/c/getFlightDetailsWidget`,
171
+ every widget property bound two levels deep as `{!$attrs.outputValues.flightInfo.<property>}` (not
172
+ one level, which would bind to an unresolvable object) → **pass**.
173
+ - `field-trace`: INVOCABLE_FIELDS (top-level, per the gate's literal definition) = `flightInfo` —
174
+ a single nested-object field, not a primitive. Because it resolves to `@apexClassType/...` (see
175
+ `clt-reference-integrity` above), it expands to its referenced class's own leaf fields before
176
+ comparing against the widget: `flightId, origin, destination, departureTime, arrivalTime, price`.
177
+ Widget properties match this expanded leaf set exactly; INVENTED empty; OMITTED empty → **pass**.
178
+
179
+ ## Notes
180
+
181
+ - **Why not a single-level CLT.** Typing `flightInfo` as `{"type":"object"}` on the response CLT
182
+ produces an opaque blob with no leaf fields — not renderable, and rejected by intent even where the
183
+ CLT metaschema would technically accept a generic object. The `@apexClassType/<ns>__<OuterClass>$<InnerClass>`
184
+ reference is what actually deploys (verified against a hand-fixed, successfully deployed bundle).
185
+ - **This is additive, not a replacement.** A response class with only primitive `@InvocableVariable`
186
+ fields (the `AccountSummary`/`GetOrderStatus` examples) still uses the flat mapping tables
187
+ unchanged. Check each field independently — a response class can mix primitive and Apex-class-typed
188
+ fields.
189
+ - **`List<ApexClass>` fields** (e.g. `SearchFlightsAction.FlightSearchResponse.availableFlights`, a
190
+ `List<Flight>`) are out of scope for this beta single-response flow — surface them in the build
191
+ plan the same way a `maxOccurs > 1` scalar is surfaced, rather than emitting a schema for them.