Migrating from Form Schema
If your process definitions already use stateway:FormSchema on userTask elements, you don't need to change anything. Stateway is fully backward-compatible.
How the Two Coexist
| Configuration | Validation Authority | Form Rendering |
|---|---|---|
Only stateway:FormSchema (legacy) | formSchema JSON Schema | formSchema fields |
Only ioSpecification | ioSpecification types via itemDefinition | Auto-generated from dataInput types |
| Both present | ioSpecification (type authority) | formSchema as UI decoration |
Incremental Migration
Add ioSpecification to a single task at a time without touching the rest of the process:
Before (legacy — only stateway:FormSchema):
<bpmn:userTask id="review" name="Review Application">
<bpmn:extensionElements>
<stateway:FormSchema>{"type":"object","properties":{"decision":{"type":"string","title":"Decision"}}}</stateway:FormSchema>
</bpmn:extensionElements>
</bpmn:userTask>
After (typed, with formSchema as a UI hint):
<!-- At <bpmn:definitions> level: -->
<bpmn:itemDefinition id="LoanStatus" structureRef="stateway:inline">
<bpmn:extensionElements>
<stateway:schema kind="enum"><![CDATA[
{ "type": "string", "enum": ["approved", "rejected", "more_info"] }
]]></stateway:schema>
</bpmn:extensionElements>
</bpmn:itemDefinition>
<!-- In the process: -->
<bpmn:userTask id="review" name="Review Application">
<bpmn:ioSpecification>
<bpmn:dataOutput id="out_decision" name="decision" itemSubjectRef="LoanStatus" />
<bpmn:outputSet>
<bpmn:dataOutputRefs>out_decision</bpmn:dataOutputRefs>
</bpmn:outputSet>
</bpmn:ioSpecification>
<!-- formSchema kept as UI hint: widget choice, label, help text -->
<bpmn:extensionElements>
<stateway:FormSchema>{"type":"object","properties":{"decision":{"type":"string","enum":["approved","rejected","more_info"],"title":"Decision"}}}</stateway:FormSchema>
</bpmn:extensionElements>
</bpmn:userTask>
The ioSpecification validates the submitted value against the LoanStatus enum. The formSchema tells the Frontend Gateway to render a dropdown with those options and a specific label.
Automatic Form Generation
When a task has ioSpecification but no formSchema, the Frontend Gateway generates a form automatically from the dataInput declarations and their itemDefinition schemas:
itemDefinition type | Generated widget |
|---|---|
xsd:string | Text input |
xsd:decimal / xsd:int | Number input |
xsd:boolean | Checkbox |
xsd:date / xsd:dateTime | Date picker |
Inline enum | Dropdown with enum values |
Inline object | One field group per property (recursively) |
Inline isCollection | Repeating field group |
You can remove formSchema entirely once you've added ioSpecification, and forms will still render correctly.
Validation Priority
If both are present and they conflict (e.g., formSchema allows any string but ioSpecification restricts to an enum), the ioSpecification wins for validation. The formSchema is UI metadata only in this case.
No Migration Deadline
There is no deprecation timeline for formSchema. Existing definitions continue to work indefinitely without changes. Migrate at your own pace, one task at a time.
Adding ioSpecification to a task that previously only had formSchema is a behavioral change: the engine now enforces types on completion. Test with invalid payloads before deploying to production to confirm error paths behave as expected.