FormFlow & Branching
The formFlow section defines the order of questions and how the form branches based on user choices. It's a tree structure where each node is either a field (question) or an option (branch choice).
Linear Flow
The simplest form has no branching — fields are asked in sequence:
formFlow:
- name: {}
- email: {}
- phone: {}
{} marks a leaf node (no children). Fields are asked in the order they appear.
Single-Level Branching
A field with options creates a branch. Each option can have its own child fields:
formFlow:
- serviceType:
cleaning:
- roomCount: {}
- preferredDate: {}
maintenance:
- issueType: {}
- urgency: {}
- name: {}
- email: {}
What happens:
- User is asked
serviceType - If "cleaning" → ask
roomCount, thenpreferredDate - If "maintenance" → ask
issueType, thenurgency - Then ask
nameandemail(common to all paths)
Fields after a branching field (like name and email above) are common to all paths. They're always asked regardless of which branch was chosen.
Nested Branching
Branches can nest arbitrarily deep:
formFlow:
- serviceType:
cleaning:
- cleaningType:
deep:
- specialRequests: {}
regular:
- frequency:
weekly: {}
biweekly: {}
monthly: {}
maintenance:
- issueType: {}
User journey (cleaning → deep):
serviceType→ "cleaning"cleaningType→ "deep"specialRequests→ (free text)- Continue to common fields after
serviceType
User journey (cleaning → regular → biweekly):
serviceType→ "cleaning"cleaningType→ "regular"frequency→ "biweekly" (leaf — no more branch children)- Continue to common fields
The LLM follows a depth-first strategy: after the user answers a branching field, it fully explores that branch's children before continuing to sibling or parent-level fields.
Node Type Alternation
The parser alternates between FIELD and OPTION nodes based on depth in the tree:
| Depth | Node Type | Example |
|---|---|---|
| 0 | (root) | Virtual root node |
| 1 | FIELD | serviceType, name, email |
| 2 | OPTION | cleaning, maintenance |
| 3 | FIELD | roomCount, cleaningType, issueType |
| 4 | OPTION | deep, regular |
| 5 | FIELD | specialRequests, frequency |
Rule: Odd depth = FIELD (question), Even depth = OPTION (branch choice).
This means:
- Top-level items (depth 1) are always questions
- Their children (depth 2) are always option values
- Option children (depth 3) are questions again
- And so on...
Info Nodes in Branches
Option nodes (even depth) require a fieldDefinition with type: info. These provide descriptive context about the branch option:
fieldDefinitions:
homeCleaning:
type: info
promptKey: services.homeCleaning.prompt
labelKey: services.homeCleaning.label
descriptionKey: services.homeCleaning.description
In the rendered schema, this appears as an INFO: annotation after the first child field of the option:
- preferredDate [DATE, required, show_when: serviceType = homeCleaning] - "What is your preferred date?"
INFO: "Comprehensive home cleaning service"
Options Sources
Options for a select or smartSelect field can come from two sources:
1. FormFlow Structure (Primary)
Keys under the field name in formFlow automatically become options:
formFlow:
- serviceType:
cleaning: [...] # "cleaning" is an option
maintenance: [...] # "maintenance" is an option
No options list needed in fieldDefinitions — they're derived from the tree.
2. Explicit Options List (Fallback)
If a field has no children in formFlow, options come from the options property:
fieldDefinitions:
areaSize:
type: smartSelect
options: ["0-49", "50-59", "60-69", "70-79", "80+"]
When both sources exist, formFlow-derived options take priority over explicit options lists.
Complete Example
A realistic cleaning service form with 2 levels of branching:
formFlow:
- serviceType: # Level 1: FIELD (branching)
homeCleaning: # Level 2: OPTION
- preferredDate: {} # Level 3: FIELD (leaf)
- preferredTime: {} # Level 3: FIELD (leaf)
windowCleaning: # Level 2: OPTION
- windowType: # Level 3: FIELD (branching)
insideOutside: # Level 4: OPTION (leaf)
outsideOnly: # Level 4: OPTION (leaf)
- windowCount: {} # Level 3: FIELD (leaf)
- preferredDate: {} # Level 3: FIELD (leaf)
- name: {} # Level 1: FIELD (common, always asked)
- email: {} # Level 1: FIELD (common, always asked)
- phone: {} # Level 1: FIELD (common, always asked)
Path 1 (home cleaning): serviceType → preferredDate → preferredTime → name → email → phone
Path 2 (window, outside only): serviceType → windowType → windowCount → preferredDate → name → email → phone
Both paths share the common fields name, email, phone at the end.