@salesforce/afv-skills 1.48.0 → 1.50.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 (31) hide show
  1. package/package.json +1 -1
  2. package/skills/education-cloud-multi-campus-configure/SKILL.md +0 -5
  3. package/skills/field-service-data-capture-form-deployer-configure/SKILL.md +180 -0
  4. package/skills/field-service-data-capture-form-deployer-configure/examples/inventory-transfer-spec.json +135 -0
  5. package/skills/field-service-data-capture-form-deployer-configure/examples/sample-spec.json +140 -0
  6. package/skills/field-service-data-capture-form-deployer-configure/examples/sectioned-spec.json +64 -0
  7. package/skills/field-service-data-capture-form-deployer-configure/references/field-types.md +297 -0
  8. package/skills/field-service-data-capture-form-deployer-configure/references/flow-metadata-json.md +243 -0
  9. package/skills/field-service-data-capture-form-deployer-configure/references/post-screen-automation.md +127 -0
  10. package/skills/field-service-data-capture-form-designer-configure/SKILL.md +110 -0
  11. package/skills/field-service-data-capture-form-designer-configure/references/extraction-from-image.md +244 -0
  12. package/skills/field-service-data-capture-form-designer-configure/references/extraction-from-prompt.md +222 -0
  13. package/skills/field-service-data-capture-form-editor-configure/SKILL.md +134 -0
  14. package/skills/field-service-data-capture-reference-configure/SKILL.md +762 -0
  15. package/skills/field-service-data-capture-reference-configure/examples/DataCapture_Showcase.flow-meta.xml +2022 -0
  16. package/skills/field-service-data-capture-reference-configure/examples/Data_Capture_All_Components.flow-meta.xml +2170 -0
  17. package/skills/field-service-data-capture-reference-configure/examples/Repeater_with_prepopulation.flow-meta.xml +231 -0
  18. package/skills/field-service-foundation-setup-designer-get/SKILL.md +135 -0
  19. package/skills/field-service-mobile-branding-configure/SKILL.md +133 -0
  20. package/skills/field-service-mobile-branding-configure/examples/dark-blue-scheme.json +16 -0
  21. package/skills/field-service-mobile-branding-configure/examples/salesforce-default-scheme.json +16 -0
  22. package/skills/field-service-mobile-branding-configure/references/color-fields.md +66 -0
  23. package/skills/field-service-mobile-branding-configure/references/contrast-validation.md +55 -0
  24. package/skills/field-service-mobile-branding-configure/references/derivation-methodology.md +83 -0
  25. package/skills/field-service-objective-designer-configure/SKILL.md +342 -0
  26. package/skills/field-service-prework-brief-deployer-configure/SKILL.md +83 -0
  27. package/skills/field-service-scheduling-policy-designer-query/SKILL.md +121 -0
  28. package/skills/field-service-setup-orchestrator-get/SKILL.md +48 -0
  29. package/skills/field-service-sobject-create-configure/SKILL.md +56 -0
  30. package/skills/field-service-voice-to-form-configure/SKILL.md +650 -0
  31. package/skills/field-service-work-rule-designer-configure/SKILL.md +303 -0
@@ -0,0 +1,297 @@
1
+ # Field types reference
2
+
3
+ Mapping of intermediate-JSON `fieldType` to the runtime component used in the deployed Flow. The `Flow strategy` column is the `fieldType` value in the Flow `Metadata` JSON (see [flow-metadata-json.md](flow-metadata-json.md)). **All names here have been verified against a live Field Service org by retrieving real flows and deploying probe flows** (May 2026).
4
+
5
+ > **Picking a type:** classify from the actual control on the form, not from the field's label or name. A text line labeled "Signature" is `ShortText`. A textarea labeled "Photo of damage" is `LongText`. Only emit `Signature`, `UploadFile`, `UploadImage`, `FileView`, `Address`, `Matrix`, or `Repeater` when the source shows the corresponding widget (sign-here pad, file/camera picker, compound address, row-shared grid, user-added rows table) — or when the user explicitly describes that widget in prose. See the "control evidence" table in `extraction-prompt.md`.
6
+
7
+ ## Verified types
8
+
9
+ | `fieldType` | `extensionName` | Flow strategy |
10
+ |-----------------|------------------------------------------------|--------------------------|
11
+ | `ShortText` | `runtime_service_fieldservice:dcTextInput` | `ComponentInstance` |
12
+ | `LongText` | `runtime_service_fieldservice:dcLongText` | `ComponentInstance` |
13
+ | `Name` | `runtime_service_fieldservice:dcName` | `ComponentInstance` |
14
+ | `Email` | `runtime_service_fieldservice:dcEmail` | `ComponentInstance` |
15
+ | `Phone` | `runtime_service_fieldservice:dcPhone` | `ComponentInstance` |
16
+ | `Numeric` | `runtime_service_fieldservice:dcNumeric` | `ComponentInstance` |
17
+ | `Counter` | `runtime_service_fieldservice:dcCounter` | `ComponentInstance` (supports `min`, `max`, `value` numeric params) |
18
+ | `Date` | `runtime_service_fieldservice:dcDate` | `ComponentInstance` |
19
+ | `DateTime` | `runtime_service_fieldservice:dcDateTime` | `ComponentInstance` |
20
+ | `Checkbox` | `runtime_service_fieldservice:dcCheckbox` | `ComponentInstance` |
21
+ | `Toggle` | `runtime_service_fieldservice:dcToggle` | `ComponentInstance` |
22
+ | `Picklist` | `runtime_service_fieldservice:dcPicklist` | `ComponentChoice` |
23
+ | `Radio` | `runtime_service_fieldservice:dcRbGroup` | `ComponentChoice` (verified May 2026 via retrieved Inventory_Transfer flow) |
24
+ | `CheckboxGroup` | `runtime_service_fieldservice:dcCbGroup` | `ComponentMultiChoice` |
25
+ | `Signature` | `runtime_service_fieldservice:dcSignature` | `ComponentInstance` (auto-wires `parentRecordId` and `recordId` to the standard input variables) |
26
+ | `UploadFile` | `runtime_service_fieldservice:dcUpFile` | `ComponentInstance` (auto-wires `recordId` → `parentRecordId`) |
27
+ | `UploadImage` | `runtime_service_fieldservice:dcUpImage` | `ComponentInstance` (auto-wires `recordId` → `parentRecordId`) |
28
+ | `Images` | `runtime_service_fieldservice:dcImages` | `ComponentInstance` (gallery viewer, auto-wires `recordId` → `parentRecordId`) |
29
+ | `Address` | `runtime_service_fieldservice:dcAddress` | `ComponentInstance` (compound street/city/state/zip widget) |
30
+ | `Matrix` | `runtime_service_fieldservice:dcMatrix` | `ComponentMultiChoice` (column choices via `<choiceReferences>`; row labels via `questions` JSON-array input parameter) |
31
+ | `Lookup` | `runtime_service_fieldservice:dcLookup` | `ComponentInstance` (requires `lookupObject`; optional `lookupSearchFields`, `lookupMulti`) |
32
+ | `FileView` | `runtime_service_fieldservice:dcFileView` | `ComponentInstance` (requires `fileName` — static asset filename without extension) |
33
+ | `DisplayText` | (native — no extension) | native `DisplayText` |
34
+ | `Repeater` | (native — no extension; nested `<fields>`) | native `Repeater` (verified May 2026; supports any `ComponentInstance` child) |
35
+
36
+ ## Falls back to a placeholder
37
+
38
+ | `fieldType` | Falls back to | Why |
39
+ |--------------|---------------------------------------------|-----|
40
+ | `Lookup` *with no `lookupObject`* | `dcTextInput` w/ `[Lookup — set objectApiName in Flow Builder]` prefix | `dcLookup` deploys but won't render without an `objectApiName`. Supply `lookupObject` in the spec or the admin must wire it up in Flow Builder. |
41
+ | `FileView` *with no `fileName`* | `dcTextInput` w/ `[FileView — set fileName in Flow Builder]` prefix | `dcFileView` needs a static asset `fileName`. |
42
+
43
+ ## Required Flow boilerplate
44
+
45
+ A Data Capture Flow won't deploy without these keys in `Metadata`:
46
+
47
+ 1. **`environments`** must include `Offline`:
48
+ ```json
49
+ "environments": ["Offline"]
50
+ ```
51
+ 2. **Three input variables** (Text, Available for Input):
52
+ ```json
53
+ "variables": [
54
+ { "name": "parentObjectType", "dataType": "String", "isCollection": false, "isInput": true, "isOutput": false },
55
+ { "name": "parentRecordId", "dataType": "String", "isCollection": false, "isInput": true, "isOutput": false },
56
+ { "name": "recordId", "dataType": "String", "isCollection": false, "isInput": true, "isOutput": false }
57
+ ]
58
+ ```
59
+ 3. **`processType`** must be `DataCaptureFlow`.
60
+
61
+ ## Choice/MultiChoice fields
62
+
63
+ For `Picklist`, `Radio` (`dcRbGroup`), and `CheckboxGroup`, options are NOT inline parameters. They are top-level `choices` entries referenced from the field via a `choiceReferences` array:
64
+
65
+ ```json
66
+ "choices": [
67
+ { "name": "Good", "choiceText": "Good", "dataType": "String", "value": { "stringValue": "Good" } }
68
+ ]
69
+ ```
70
+ ```json
71
+ {
72
+ "name": "overallCondition",
73
+ "choiceReferences": ["Good", "Fair", "Poor"],
74
+ "extensionName": "runtime_service_fieldservice:dcPicklist",
75
+ "fieldText": "Overall Condition",
76
+ "fieldType": "ComponentChoice",
77
+ "inputsOnNextNavToAssocScrn": "UseStoredValues",
78
+ "isRequired": true,
79
+ "storeOutputAutomatically": true
80
+ }
81
+ ```
82
+
83
+ Dedupe choices across the whole flow — two fields with `["Good","Fair","Poor"]` share the same three `choices` entries.
84
+
85
+ ## Repeater fields
86
+
87
+ `Repeater` is a native Flow field type — not a component. The JSON shape (children are a nested `fields` array):
88
+
89
+ ```json
90
+ {
91
+ "name": "Part",
92
+ "fieldType": "Repeater",
93
+ "fields": [
94
+ { "name": "Quantity", "extensionName": "runtime_service_fieldservice:dcCounter", "fieldType": "ComponentInstance" },
95
+ { "name": "Part_Number", "extensionName": "runtime_service_fieldservice:dcTextInput", "fieldType": "ComponentInstance" }
96
+ ],
97
+ "isRequired": false,
98
+ "styleProperties": { "verticalAlignment": { "stringValue": "top" }, "width": { "stringValue": "12" } }
99
+ }
100
+ ```
101
+
102
+ In the spec, supply children as `repeaterFields`:
103
+
104
+ ```json
105
+ {
106
+ "fieldName": "Part",
107
+ "fieldLabel": "Parts",
108
+ "fieldType": "Repeater",
109
+ "isRequired": false,
110
+ "repeaterFields": [
111
+ { "fieldName": "Quantity", "fieldLabel": "Quantity", "fieldType": "Counter", "isRequired": true, "min": 1, "value": 1 },
112
+ { "fieldName": "Part_Number", "fieldLabel": "Part Number", "fieldType": "ShortText", "isRequired": true },
113
+ { "fieldName": "Part_Description", "fieldLabel": "Part Description", "fieldType": "ShortText", "isRequired": true }
114
+ ]
115
+ }
116
+ ```
117
+
118
+ The runtime exposes the captured rows as the collection `<RepeaterName>.AddedItems`, with each row's child accessible as `<loopVar>.<ChildName>.value` inside a Loop element. See `flow-metadata-json.md` for the loop/createRecord pattern.
119
+
120
+ ## Counter min/max/value
121
+
122
+ `dcCounter` accepts these optional numeric input parameters: `min`, `max`, `value` (initial value). In the spec:
123
+
124
+ ```json
125
+ {
126
+ "fieldName": "Quantity",
127
+ "fieldLabel": "Quantity",
128
+ "fieldType": "Counter",
129
+ "isRequired": true,
130
+ "min": 1,
131
+ "max": 999,
132
+ "value": 1
133
+ }
134
+ ```
135
+
136
+ ## Specialized component spec keys
137
+
138
+ ### Signature / UploadFile / UploadImage / Images
139
+
140
+ Auto-wired — no extra spec keys needed. Inject:
141
+ - `Signature` → `parentRecordId` and `recordId` input parameters, both as `{ "elementReference": ... }` to the flow's standard input variables of the same name.
142
+ - `UploadFile` / `UploadImage` / `Images` → `recordId` input parameter as `{ "elementReference": "parentRecordId" }` (the runtime attaches the captured file/image to the parent record passed in via `parentRecordId`).
143
+
144
+ These standard input variables are present on every flow:
145
+
146
+ ```json
147
+ "variables": [
148
+ { "name": "parentObjectType", "dataType": "String", "isCollection": false, "isInput": true, "isOutput": false },
149
+ { "name": "parentRecordId", "dataType": "String", "isCollection": false, "isInput": true, "isOutput": false },
150
+ { "name": "recordId", "dataType": "String", "isCollection": false, "isInput": true, "isOutput": false }
151
+ ]
152
+ ```
153
+
154
+ So `Signature` / `UploadFile` / `UploadImage` / `Images` deploy and run end-to-end as long as the flow is launched from a standard Field Service entry point (Mobile App, Work Order, Asset, etc.) that supplies `parentRecordId`.
155
+
156
+ ### Address
157
+
158
+ No extra spec keys. Renders as `dcAddress` ComponentInstance with the field label.
159
+
160
+ ### Matrix
161
+
162
+ ```json
163
+ {
164
+ "fieldName": "PreTripChecklist",
165
+ "fieldLabel": "Pre-trip checklist",
166
+ "fieldType": "Matrix",
167
+ "isRequired": true,
168
+ "options": ["OK", "NOK", "N/A"],
169
+ "matrixRows": ["Tires", "Lights", "Brakes", "Fluids"]
170
+ }
171
+ ```
172
+
173
+ - `options` become column choices (`<choiceReferences>`), exactly like Picklist.
174
+ - `matrixRows` become per-row prompts, JSON-encoded into the `questions` input parameter as a string array.
175
+
176
+ ### Lookup
177
+
178
+ ```json
179
+ {
180
+ "fieldName": "AssetLookup",
181
+ "fieldLabel": "Asset",
182
+ "fieldType": "Lookup",
183
+ "isRequired": true,
184
+ "lookupObject": "Asset",
185
+ "lookupSearchFields": "Name, SerialNumber",
186
+ "lookupMulti": false
187
+ }
188
+ ```
189
+
190
+ - `lookupObject` (required) — the object API name to look up against.
191
+ - `lookupSearchFields` — comma-separated field API names searched by typeahead.
192
+ - `lookupMulti` (or `isMultiSelection`) — `true` to allow selecting multiple records.
193
+
194
+ If `lookupObject` is missing, the field renders as a labeled ShortText placeholder so the flow still deploys.
195
+
196
+ ### FileView
197
+
198
+ ```json
199
+ {
200
+ "fieldName": "ManualPreview",
201
+ "fieldLabel": "Manual",
202
+ "fieldType": "FileView",
203
+ "fileName": "service_manual_v3"
204
+ }
205
+ ```
206
+
207
+ `fileName` is the static-asset filename (without extension). If missing, falls back to a labeled ShortText placeholder.
208
+
209
+ ## Visibility rules
210
+
211
+ A field can be conditionally shown based on a previously-captured field. The spec shape:
212
+
213
+ ```json
214
+ {
215
+ "fieldName": "Work_Order_Number",
216
+ "fieldLabel": "Work Order Number",
217
+ "fieldType": "ShortText",
218
+ "isRequired": true,
219
+ "visibility": {
220
+ "field": "To",
221
+ "operator": "EqualTo",
222
+ "value": "Work Order"
223
+ }
224
+ }
225
+ ```
226
+
227
+ Look up the matching choice api name (e.g. `Work_Order`) and emit a `visibilityRule` on the field:
228
+
229
+ ```json
230
+ "visibilityRule": {
231
+ "conditionLogic": "and",
232
+ "conditions": [
233
+ { "leftValueReference": "Work_Order", "operator": "EqualTo", "rightValue": { "booleanValue": true } }
234
+ ]
235
+ }
236
+ ```
237
+
238
+ > Note: Field Service's runtime evaluates visibility against the choice's *boolean selection state*, not the parent field's string value. The `leftValueReference` is therefore the **choice api name**, not the field name. If `visibility.value` doesn't match any known choice, warn and skip the rule.
239
+
240
+ ## Field name rules
241
+
242
+ - `fieldName` must match `^[A-Za-z][A-Za-z0-9_]*$`, ≤ 80 chars, unique per flow.
243
+ - Choice api names are derived from the option's display text (sanitized).
244
+ - Screen api names are derived as `Screen_<PascalCaseScreenKey>`.
245
+
246
+ ## Sections
247
+
248
+ A Section is a visual grouping inside a screen, rendered as a Salesforce Flow `RegionContainer` (one full-width Region column) with a header bar showing the section label. Use sections **only when the source explicitly shows them** (visible group headers in an image/PDF, or the user describing named sub-sections in prose). A flat field list is the right default.
249
+
250
+ To declare sections, mix section-header items into a screen's field list. A section header is an item with a `section` key and **no** `fieldName`:
251
+
252
+ ```json
253
+ {
254
+ "screens": {
255
+ "AssetInspection": [
256
+ { "section": "Asset Details" },
257
+ { "fieldName": "AssetName", "fieldLabel": "Asset Name", "fieldType": "ShortText" },
258
+ { "fieldName": "Manufacturer", "fieldLabel": "Manufacturer", "fieldType": "Picklist", "options": ["Demag","Konecranes"] },
259
+
260
+ { "section": "Inspection" },
261
+ { "fieldName": "Condition", "fieldLabel": "Condition", "fieldType": "Picklist", "options": ["Pass","Fail"] },
262
+ { "fieldName": "Notes", "fieldLabel": "Notes", "fieldType": "LongText" }
263
+ ]
264
+ }
265
+ }
266
+ ```
267
+
268
+ Rules:
269
+ - A `section` item starts a new section. All subsequent fields belong to it until the next `section` item or the end of the screen.
270
+ - Fields appearing **before** the first `section` item are bucketed into an implicit section labeled `"General"`.
271
+ - Specs with no `section` items deploy as a flat field list — fully backward-compatible.
272
+ - Sections are **single-column, full-width** by default. Multi-column layouts are not generated; if a source shows two columns, file each field separately and let it stack.
273
+ - Section api names are auto-derived from the label (`Sec_<sanitized>`, deduped within the screen).
274
+
275
+ The emitted JSON matches the verified `SectionWithHeader` pattern from `salesforce-data-capture/examples/Data_Capture_All_Components.flow-meta.xml`:
276
+
277
+ ```json
278
+ {
279
+ "name": "Sec_Asset_Details",
280
+ "fieldText": "Asset Details",
281
+ "fieldType": "RegionContainer",
282
+ "fields": [
283
+ {
284
+ "name": "Sec_Asset_Details_Col1",
285
+ "fieldType": "Region",
286
+ "fields": [ /* AssetName, Manufacturer, etc. */ ],
287
+ "inputParameters": [
288
+ { "name": "width", "value": { "stringValue": "12" } }
289
+ ],
290
+ "isRequired": false
291
+ }
292
+ ],
293
+ "isRequired": false,
294
+ "regionContainerType": "SectionWithHeader",
295
+ "styleProperties": {}
296
+ }
297
+ ```
@@ -0,0 +1,243 @@
1
+ # Flow Metadata JSON — Data Capture Flow
2
+
3
+ A Data Capture Flow is a Salesforce Flow with `processType=DataCaptureFlow`. It is created and updated through the **Tooling API `Flow` sObject**, whose `Metadata` field is a JSON object — the same shape you get back from `GET /services/data/vXX.0/tooling/sobjects/Flow/{id}`. There is **no `.flow-meta.xml`, no zip, and no SFDX project** — the agent assembles this JSON inline and POSTs it.
4
+
5
+ The shape below is the JSON transliteration of the Metadata blob retrieved from real flows in a live Field Service org (`Data_Capture_Asset_Inspection`, `Inventory_Transfer`) and verified against that org's `Flow` describe.
6
+
7
+ ## The deploy call
8
+
9
+ ```http
10
+ POST /services/data/vXX.0/tooling/sobjects/Flow
11
+ {
12
+ "FullName": "<FlowApiName>",
13
+ "Metadata": { ...the object below... }
14
+ }
15
+ ```
16
+
17
+ - `FullName` is the Flow API name (matches `^[A-Z][A-Za-z0-9_]*$`). `FullName` is set-on-create only.
18
+ - A 201 with `success: true` returns the new Flow version id.
19
+ - To **activate on create**, set `Metadata.status: "Active"`. Default `"Draft"` so the user reviews in Flow Builder first — activation is via `Flow.Metadata.status`, not a separate `FlowDefinition` write (`FlowDefinition.ActiveVersionId` is not directly writable).
20
+ - To **update** an existing version, `PATCH /services/data/vXX.0/tooling/sobjects/Flow/{id}` with `{"Metadata": {...full metadata...}}` (the editor skill's redeploy path).
21
+
22
+ ## JSON ↔ XML mapping rule
23
+
24
+ The Metadata JSON is a mechanical transliteration of the Flow XML:
25
+
26
+ - Each XML element becomes a JSON key. `<label>X</label>` → `"label": "X"`.
27
+ - **Repeated** elements become a JSON **array**: multiple `<screens>` → `"screens": [ {...}, {...} ]`; multiple `<choiceReferences>` → `"choiceReferences": ["A","B"]`.
28
+ - Typed value wrappers keep their wrapper key: `<value><stringValue>X</stringValue></value>` → `"value": {"stringValue": "X"}`; `<booleanValue>true</booleanValue>` → `{"booleanValue": true}`; `<numberValue>1.0</numberValue>` → `{"numberValue": 1.0}`; `<elementReference>Foo</elementReference>` → `{"elementReference": "Foo"}`.
29
+ - Booleans and numbers are real JSON scalars, not strings.
30
+
31
+ ## Minimum-deployable shape
32
+
33
+ ```json
34
+ {
35
+ "processType": "DataCaptureFlow",
36
+ "environments": ["Offline"],
37
+ "areMetricsLoggedToDataCloud": false,
38
+ "label": "HVAC Compressor Inspection",
39
+ "interviewLabel": "HVAC Compressor Inspection {!$Flow.CurrentDateTime}",
40
+ "description": "Generated from spec: HVAC Compressor Inspection",
41
+ "status": "Draft",
42
+
43
+ "processMetadataValues": [
44
+ { "name": "BuilderType", "value": { "stringValue": "LightningFlowBuilder" } },
45
+ { "name": "CanvasMode", "value": { "stringValue": "AUTO_LAYOUT_CANVAS" } }
46
+ ],
47
+
48
+ "choices": [
49
+ { "name": "Good", "choiceText": "Good", "dataType": "String", "value": { "stringValue": "Good" } }
50
+ ],
51
+
52
+ "start": { "locationX": 0, "locationY": 0, "connector": { "targetReference": "Screen_HeaderInformation" } },
53
+
54
+ "screens": [
55
+ {
56
+ "name": "Screen_HeaderInformation",
57
+ "label": "Header Information",
58
+ "locationX": 0, "locationY": 0,
59
+ "allowBack": true, "allowFinish": true, "allowPause": true,
60
+ "showFooter": true, "showHeader": true,
61
+ "connector": { "targetReference": "Screen_OperatingParameters" },
62
+ "fields": [
63
+
64
+ {
65
+ "name": "jobName",
66
+ "extensionName": "runtime_service_fieldservice:dcTextInput",
67
+ "fieldType": "ComponentInstance",
68
+ "inputParameters": [
69
+ { "name": "label", "value": { "stringValue": "Job Name" } }
70
+ ],
71
+ "inputsOnNextNavToAssocScrn": "UseStoredValues",
72
+ "isRequired": true,
73
+ "storeOutputAutomatically": true,
74
+ "styleProperties": {
75
+ "verticalAlignment": { "stringValue": "top" },
76
+ "width": { "stringValue": "12" }
77
+ }
78
+ },
79
+
80
+ {
81
+ "name": "Quantity",
82
+ "extensionName": "runtime_service_fieldservice:dcCounter",
83
+ "fieldType": "ComponentInstance",
84
+ "inputParameters": [
85
+ { "name": "label", "value": { "stringValue": "Quantity" } },
86
+ { "name": "min", "value": { "numberValue": 1.0 } },
87
+ { "name": "value", "value": { "numberValue": 1.0 } }
88
+ ]
89
+ },
90
+
91
+ {
92
+ "name": "To",
93
+ "choiceReferences": ["Truck", "Work_Order"],
94
+ "extensionName": "runtime_service_fieldservice:dcRbGroup",
95
+ "fieldText": "To",
96
+ "fieldType": "ComponentChoice"
97
+ },
98
+
99
+ {
100
+ "name": "issuesIdentified",
101
+ "choiceReferences": ["Corrosion", "Wear", "Leakage"],
102
+ "extensionName": "runtime_service_fieldservice:dcCbGroup",
103
+ "fieldText": "Issues Identified",
104
+ "fieldType": "ComponentMultiChoice"
105
+ },
106
+
107
+ {
108
+ "name": "Work_Order_Number",
109
+ "extensionName": "runtime_service_fieldservice:dcTextInput",
110
+ "fieldType": "ComponentInstance",
111
+ "inputParameters": [
112
+ { "name": "label", "value": { "stringValue": "Work Order Number" } }
113
+ ],
114
+ "isRequired": true,
115
+ "visibilityRule": {
116
+ "conditionLogic": "and",
117
+ "conditions": [
118
+ { "leftValueReference": "Work_Order", "operator": "EqualTo", "rightValue": { "booleanValue": true } }
119
+ ]
120
+ }
121
+ },
122
+
123
+ {
124
+ "name": "Part",
125
+ "fieldType": "Repeater",
126
+ "fields": [
127
+ { "name": "Quantity", "extensionName": "runtime_service_fieldservice:dcCounter", "fieldType": "ComponentInstance" },
128
+ { "name": "Part_Number", "extensionName": "runtime_service_fieldservice:dcTextInput", "fieldType": "ComponentInstance" }
129
+ ],
130
+ "isRequired": false,
131
+ "styleProperties": { "verticalAlignment": { "stringValue": "top" }, "width": { "stringValue": "12" } }
132
+ },
133
+
134
+ {
135
+ "name": "safetyInstructions",
136
+ "fieldText": "<p>All personnel must wear PPE.</p>",
137
+ "fieldType": "DisplayText"
138
+ }
139
+ ]
140
+ }
141
+ ],
142
+
143
+ "decisions": [
144
+ {
145
+ "name": "Transfer_Location_Decision",
146
+ "label": "Transfer Location Decision",
147
+ "locationX": 0, "locationY": 0,
148
+ "defaultConnectorLabel": "Error",
149
+ "rules": [
150
+ {
151
+ "name": "Truck_Decision",
152
+ "conditionLogic": "and",
153
+ "conditions": [
154
+ { "leftValueReference": "To.selectedChoiceValues", "operator": "EqualTo", "rightValue": { "elementReference": "Truck" } }
155
+ ],
156
+ "connector": { "targetReference": "Get_Truck" },
157
+ "label": "Truck"
158
+ }
159
+ ]
160
+ }
161
+ ],
162
+
163
+ "recordLookups": [
164
+ {
165
+ "name": "Get_Truck",
166
+ "label": "Get Truck",
167
+ "locationX": 0, "locationY": 0,
168
+ "assignNullValuesIfNoRecordsFound": false,
169
+ "connector": { "targetReference": "Part_Transfers" },
170
+ "filterLogic": "and",
171
+ "filters": [
172
+ { "field": "Service_Resource__c", "operator": "EqualTo", "value": { "elementReference": "$User.Id" } }
173
+ ],
174
+ "object": "Location",
175
+ "outputAssignments": [
176
+ { "assignToReference": "Transfer_Destination", "field": "Id" }
177
+ ]
178
+ }
179
+ ],
180
+
181
+ "loops": [
182
+ {
183
+ "name": "Part_Transfers",
184
+ "label": "Part Transfers",
185
+ "locationX": 0, "locationY": 0,
186
+ "collectionReference": "Part.AddedItems",
187
+ "iterationOrder": "Asc",
188
+ "nextValueConnector": { "targetReference": "Transfer_Part" }
189
+ }
190
+ ],
191
+
192
+ "recordCreates": [
193
+ {
194
+ "name": "Transfer_Part",
195
+ "label": "Transfer Part",
196
+ "locationX": 0, "locationY": 0,
197
+ "connector": { "targetReference": "Part_Transfers" },
198
+ "inputAssignments": [
199
+ { "field": "Product2Id", "value": { "elementReference": "Part_Transfers.Part_Number.value" } }
200
+ ],
201
+ "object": "ProductTransfer",
202
+ "storeOutputAutomatically": true
203
+ }
204
+ ],
205
+
206
+ "variables": [
207
+ { "name": "parentObjectType", "dataType": "String", "isCollection": false, "isInput": true, "isOutput": false },
208
+ { "name": "parentRecordId", "dataType": "String", "isCollection": false, "isInput": true, "isOutput": false },
209
+ { "name": "recordId", "dataType": "String", "isCollection": false, "isInput": true, "isOutput": false },
210
+ { "name": "Transfer_Destination","dataType": "String", "isCollection": false, "isInput": false, "isOutput": false }
211
+ ]
212
+ }
213
+ ```
214
+
215
+ ## Field-rendering strategies
216
+
217
+ | Strategy | Triggered by | Key JSON differences |
218
+ |----------|--------------|----------------------|
219
+ | `ComponentInstance` | most types: TextInput, Email, Phone, Numeric, Counter, Date, DateTime, Checkbox, Toggle, LongText | `"fieldType": "ComponentInstance"`, label via `inputParameters` |
220
+ | `ComponentChoice` | Picklist, Radio (`dcRbGroup`) | `"fieldType": "ComponentChoice"`, label via `fieldText`, options via `choiceReferences` array |
221
+ | `ComponentMultiChoice` | CheckboxGroup | `"fieldType": "ComponentMultiChoice"`, label via `fieldText`, options via `choiceReferences` array |
222
+ | native `DisplayText` | DisplayText | `"fieldType": "DisplayText"`, body via `fieldText` (HTML), no `extensionName` |
223
+ | native `Repeater` | Repeater | `"fieldType": "Repeater"`, child fields via nested `fields` array, no `extensionName` |
224
+
225
+ ## Requiredness — `isRequired` only, never a `required` inputParameter
226
+
227
+ Mark a mandatory field with the field-level `"isRequired": true` key **only**. Do **not** add `{ "name": "required", ... }` to `inputParameters` — `dcName`, `dcSignature`, and other Field Service components reject it, and the deploy fails with `We can't find this input attribute: 'required'`. This is the single most common deploy error; the examples above intentionally carry `isRequired` with no `required` inputParameter.
228
+
229
+ ## Connector pattern
230
+
231
+ - `start.connector` points to screen #1.
232
+ - Each screen — *except* the last — has a `connector` to the next screen.
233
+ - The **last screen** connects to `postScreen` (decision/lookup/loop/create) if present, otherwise omits `connector`.
234
+ - All screens set `"allowFinish": true`.
235
+
236
+ ## Things deliberately not generated
237
+
238
+ - No formulas, assignments, or text templates.
239
+ - No nested loops or decisions inside loops.
240
+ - No subflows.
241
+ - No `dcLookup` without an `objectApiName` (specs missing `lookupObject` fall back to a labeled TextInput placeholder).
242
+ - No `dcFileView` without a `fileName` (specs missing `fileName` fall back to a labeled TextInput placeholder).
243
+ - No visual polish HTML (banners, progress bars, callouts).
@@ -0,0 +1,127 @@
1
+ # Post-screen automation: decision → lookup → loop → createRecord
2
+
3
+ The Inventory_Transfer flow in the demo org shows the canonical "do something with the captured data" chain. After the last screen, the flow:
4
+
5
+ 1. **Decides** which branch to take based on a Radio answer (Truck vs Work Order).
6
+ 2. **Looks up** a record per branch (the truck Location for the user, or the WorkOrder).
7
+ 3. **Stores** the resulting Id into a private variable (`Transfer_Destination`).
8
+ 4. **Loops** over the Repeater's `AddedItems` collection.
9
+ 5. **Creates** one Salesforce record per row, mapping the row's child-field values onto the target object.
10
+
11
+ This file documents the spec shape that drives that XML.
12
+
13
+ ## Spec extension
14
+
15
+ Add a top-level `postScreen` block:
16
+
17
+ ```json
18
+ {
19
+ "formTitle": "Inventory Transfer",
20
+ "formType": "Inventory",
21
+ "screens": { ... },
22
+ "postScreen": {
23
+ "variables": [
24
+ { "name": "Transfer_Destination", "type": "String" }
25
+ ],
26
+ "decision": {
27
+ "name": "Transfer_Location_Decision",
28
+ "label": "Transfer Location Decision",
29
+ "defaultLabel": "Error",
30
+ "branches": [
31
+ {
32
+ "name": "Truck_Decision",
33
+ "label": "Truck",
34
+ "when": { "field": "To", "operator": "EqualTo", "value": "Truck" },
35
+ "then": "Get_Truck"
36
+ },
37
+ {
38
+ "name": "Work_Order_Decision",
39
+ "label": "Work Order",
40
+ "when": { "field": "To", "operator": "EqualTo", "value": "Work Order" },
41
+ "then": "Get_WO"
42
+ }
43
+ ]
44
+ },
45
+ "recordLookups": [
46
+ {
47
+ "name": "Get_Truck",
48
+ "label": "Get Truck",
49
+ "object": "Location",
50
+ "filters": [
51
+ { "field": "Service_Resource__c", "operator": "EqualTo", "valueRef": "$User.Id" }
52
+ ],
53
+ "outputAssignments": [
54
+ { "assignTo": "Transfer_Destination", "field": "Id" }
55
+ ],
56
+ "next": "Part_Transfers"
57
+ },
58
+ {
59
+ "name": "Get_WO",
60
+ "label": "Get WO",
61
+ "object": "WorkOrder",
62
+ "filters": [
63
+ { "field": "Id", "operator": "EqualTo", "valueRef": "Work_Order_Number.value" }
64
+ ],
65
+ "outputAssignments": [
66
+ { "assignTo": "Transfer_Destination", "field": "LocationId" }
67
+ ],
68
+ "next": "Part_Transfers"
69
+ }
70
+ ],
71
+ "loop": {
72
+ "name": "Part_Transfers",
73
+ "label": "Part Transfers",
74
+ "collection": "Part.AddedItems",
75
+ "next": "Transfer_Part"
76
+ },
77
+ "recordCreates": [
78
+ {
79
+ "name": "Transfer_Part",
80
+ "label": "Transfer Part",
81
+ "object": "ProductTransfer",
82
+ "next": "Part_Transfers",
83
+ "inputAssignments": [
84
+ { "field": "DestinationLocationId", "valueRef": "Transfer_Destination" },
85
+ { "field": "IsReceived", "boolean": true },
86
+ { "field": "OwnerId", "valueRef": "$User.Id" },
87
+ { "field": "Product2Id", "valueRef": "Part_Transfers.Part_Number.value" },
88
+ { "field": "QuantityReceived", "valueRef": "Part_Transfers.Quantity.value" },
89
+ { "field": "QuantitySent", "valueRef": "Part_Transfers.Quantity.value" },
90
+ { "field": "ReceivedById", "valueRef": "$User.Id" }
91
+ ]
92
+ }
93
+ ]
94
+ }
95
+ }
96
+ ```
97
+
98
+ ## How the converter wires it up
99
+
100
+ When `postScreen` is present:
101
+
102
+ - The **last screen's connector** points to the decision's `name` (or, if no decision, the first lookup, or the loop, or the first recordCreate — whichever is first in the chain).
103
+ - The decision emits `<decisions>` with one `<rules>` per branch. Each rule's `connector` targets `then`. The `defaultConnectorLabel` becomes the catch-all branch label (no connector means the flow ends on the default path).
104
+ - Each `recordLookups` emits its filter list, output assignments, and a `connector` to its `next`.
105
+ - The `loop` emits `<loops>` with `collectionReference`, `nextValueConnector` to its `next`. The "no more items" path falls through (no connector).
106
+ - Each `recordCreates` emits its input assignments and a `connector` back to the loop (so iteration continues). The loop's "default" (after items finish) is what naturally terminates the flow.
107
+ - All non-input variables in `postScreen.variables` are emitted alongside the three required `parentObjectType` / `parentRecordId` / `recordId` variables.
108
+
109
+ ## Spec → XML reference card
110
+
111
+ | Spec key | Emits |
112
+ |---|---|
113
+ | `valueRef` | `<elementReference>...</elementReference>` |
114
+ | `value` (string) | `<stringValue>...</stringValue>` |
115
+ | `boolean` | `<booleanValue>true|false</booleanValue>` |
116
+ | `number` | `<numberValue>...</numberValue>` |
117
+
118
+ Filter / decision-condition operators that have been seen in real flows: `EqualTo`, `NotEqualTo`, `GreaterThan`, `LessThan`, `IsNull`, `Contains`. The converter passes them through as-is — Salesforce will reject anything invalid.
119
+
120
+ ## Limitations
121
+
122
+ - Only one decision, one loop, and one chain of lookups+creates per spec. Real flows can have many; that's not in scope here.
123
+ - No `assignments` element (the Inventory_Transfer flow uses `recordLookups` outputAssignments instead, which we replicate).
124
+ - No formula resources or text templates.
125
+ - No nested loops, no decisions inside loops.
126
+
127
+ If you need any of the above, deploy what the skill produces and finish wiring in Flow Builder.