Skip to main content

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:

  1. User is asked serviceType
  2. If "cleaning" → ask roomCount, then preferredDate
  3. If "maintenance" → ask issueType, then urgency
  4. Then ask name and email (common to all paths)
info

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):

  1. serviceType → "cleaning"
  2. cleaningType → "deep"
  3. specialRequests → (free text)
  4. Continue to common fields after serviceType

User journey (cleaning → regular → biweekly):

  1. serviceType → "cleaning"
  2. cleaningType → "regular"
  3. frequency → "biweekly" (leaf — no more branch children)
  4. Continue to common fields
Depth-First Traversal

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:

DepthNode TypeExample
0(root)Virtual root node
1FIELDserviceType, name, email
2OPTIONcleaning, maintenance
3FIELDroomCount, cleaningType, issueType
4OPTIONdeep, regular
5FIELDspecialRequests, 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+"]
Priority

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.