mirror of
https://github.com/forcedotcom/afv-library.git
synced 2026-08-09 00:42:46 +08:00
4.4 KiB
4.4 KiB
Build Plan Format
Use this template in Phase 3 to print the plan before proceeding. Fill every section. Do not abbreviate. Do not print inside a code fence the user might mistake for output — the plan is conversational.
MCP Tool Widget Build Plan: <widgetName>
PLAN: <one line in developer-facing terms, e.g.:
"Render the GetAccountSummary MCP tool output with an account-summary widget">
TOOL / SOURCE:
Tool API name: <toolApiName>
Payload source: <action: Actions REST describe of <ActionApiName> | apex: class <ClassName> | sample: pasted tool-output>
Response class FQN: <namespace>__<ClassName>.<ResponseClass> # apex source only; omit for action/sample
LIGHTNING TYPES (two object-based CLTs of equal standing — named for what each models, not by role; both carry root-level "lightning:tags": ["mcp"]):
Response CLT:
Name: <responseCLT> # convention: <toolApiName>Response
Path: <pkgDir>/lightningTypes/<responseCLT>/schema.json
Properties: <field: lightning:type, ...> # 1:1 with response @InvocableVariable fields
Envelope CLT:
Name: <toolCLT> # convention: <toolApiName>
Path: <pkgDir>/lightningTypes/<toolCLT>/schema.json
Renderer (default, at bundle root — wires the widget): <pkgDir>/lightningTypes/<toolCLT>/renderer.json
Envelope properties: actionName (text), isSuccess (boolean), outputValues (c__<responseCLT>), <plus any others>
WIDGET:
Name: <widgetName> # convention: <toolApiName>Widget
Output:
<pkgDir>/uiWidgets/<widgetName>/<widgetName>.json
<pkgDir>/uiWidgets/<widgetName>/schema.json
<pkgDir>/uiWidgets/<widgetName>/<widgetName>.uiwidget-meta.xml
Schema source: derived from the response field list (name + primitive type) — a standalone contract, not tied to any Lightning Type
Renderer binding: each widget attribute maps to {!$attrs.outputValues.<field>}
Layout intent: <one-line description of the widget composition>
Properties omitted: <response fields the widget intentionally drops, with rationale — or "none">
# actionName/isSuccess never appear here — they are envelope-only and never candidates for the widget.
# Default omissions to declare when present in the response: isSuccess, errorMessage, status, message
# (operational/status indicators, not display data) — each needs its own one-line rationale, not just the field name.
SUB-SKILLS THAT WILL RUN:
platform-custom-lightning-type-generate (response CLT, then envelope CLT)
platform-widget-generate (widget bundle)
(renderer.json authored inline in the envelope CLT by this orchestrator)
VALIDATIONS THAT WILL RUN AFTER GENERATION:
Widget bundle self-validation (run by platform-widget-generate):
- widget schema.json parses and has the required root keys
- every leaf in properties has a lightning:type
- every {!$attrs.X} resolves to a widget schema property
- <name>.uiwidget-meta.xml is well-formed, root <UiWidgetBundle>, declares <widgetType>JSON</widgetType>
Cross-skill checks (run by this orchestrator):
- clt-reference-integrity: envelope CLT outputValues → c__<responseCLT>; response CLT exists; no $schema/items
- renderer-wires-widget: envelope CLT renderer.json (bundle root) references the widget via @widget/c/<widgetName>,
binding every widget property as {!$attrs.outputValues.<property>}
- field-trace (advisory): print response @InvocableVariable fields and widget schema properties; print the diff.
Invented widget fields fail; omissions not declared above warn.
GENERATION ORDER: response CLT → widget → envelope CLT (response CLT must exist before the envelope CLT references it).
----------------------------------------------------------------
Proceeding unless you push back (reply "no", "stop", "change X"). The plan above is the record of intent.
Notes for the model
- If the user replies with edits or declines, revise the plan and reprint. Do not assume which sections changed.
- Approval applies only to the plan as printed. A later request for another tool starts a new planning cycle.
- "Properties omitted" makes intentional drops explicit — e.g.
status,message, internal IDs that do not belong on the render surface. - If the payload source is a pasted sample that nests under
outputValues.data, record that extra level here — it changes the response CLT and every renderer binding to{!$attrs.outputValues.data.<field>}.