Core lifecycle & guarantees

Compilation establishes a valid structure. Evaluation settles field values, behavior, and errors. Rendering consumes the resulting state.

1. Compile and validate structure

Core checks the schema version, registered field/renderer types, keys and namespaces, nesting, type-specific settings, validators, source references, and rule dependencies. Templates are checked even when a repeater has no instances. Groups organize fields; repeaters create runtime collections.

References must resolve in a compatible value domain. Value-producing dependencies must form a directed acyclic graph; a value cycle rejects the schema. Visibility/error observer loops do not create value cycles. No arbitrary code in a schema is evaluated.

2. Populate and settle values

Initial data fills every leaf and repeater before evaluation. Missing data uses schema defaults, then type defaults; an explicit null is meaningful. Rules that produce values settle before visibility/error observers and validation. Declaration order is preserved for ordered rules and actions.

A compiled, renderable schema is different from valid entered data. Ordinary validator failures produce editable state. Unknown keys, incompatible types, invalid references, malformed arrays, or processing-limit failures reject loading rather than install partial data.

3. Apply a user edit

The worker serializes commands. An edit normalizes the field value, queues its dependent work, applies resulting rules, and updates validation. Core tracks affected state; ordinary worker edit responses contain affected field records rather than the entire form.

JSON
{
  "kind": "delta",
  "generation": "form-generation",
  "baseRevision": "12",
  "revision": "13",
  "fields": {
    "name": {
      "value": "Ada",
      "hidden": false,
      "errors": []
    }
  }
}

This is an illustrative transport fragment, not an application API call. The JS mirror applies deltas against the matching generation and base revision. A revision mismatch triggers an explicit authoritative snapshot request. Loading, structural operations, submission/export, and recovery have their own snapshot boundaries.

The performance goal is that an isolated edit scales with affected dependencies rather than total form size. End-to-end latency also depends on rendering, validation, and the dependencies affected by an edit; it is not guaranteed to be identical for every form size.

4. Integrate asynchronous results

Core identifies referenced sources and emits effects. JavaScript executes requests and sends ready data or errors back with request/source revisions. Stale results cannot replace a newer generation. Loading/readiness is part of core state; network code and credentials remain in JavaScript.

Successful results are normalized for the target value/options domain. Selection reconciliation, rules, and validation run after integration. Dependent selects discard invalid old selections; a valid restored draft selection survives initial option loading.

5. Change repeater structure

Add initializes new instances and nested defaults. Remove drops that instance and descendants. Reorder retains stable identities and surviving values/errors while updating position-based keys and references. Cardinality errors stay editable; data is not padded or truncated to bounds.

Structural mutations that introduce invalid references, cycles, or processing-limit failures roll back atomically. See Repeaters for scoped current/numeric references and current safety limits.

6. Validate and submit

Hidden and disabled fields retain values without blocking validation. Read-only fields and inactive sections still validate. Required means a value is present; false and zero satisfy required. “Must be true” is a separate boolean constraint. Behavior and validator errors have separate ownership.

Submit validates, prepares pending submit-time uploads, and supplies meaningful values from the accepted snapshot. Loading, unresolved sources, source failures, or upload failures prevent a successful submit. The application callback owns final persistence.

Observable state

JSON
{
  "schema": {
    "schemaVersion": "sprout",
    "form": [
      {
        "type": "text",
        "key": "name",
        "validators": [
          {
            "kind": "required"
          }
        ]
      }
    ]
  },
  "state": {
    "fields": {
      "name": {
        "value": null,
        "hidden": false,
        "errors": [
          {
            "messageKey": "tg_validation_required"
          }
        ]
      }
    },
    "repeaters": {}
  },
  "values": {
    "name": null
  },
  "valid": false
}

This is a shortened illustrative snapshot. Values contain submitted data; state also contains visibility, message keys, source/upload readiness, options, and collection metadata. Renderer navigation and focus are local UI state. Core messages contain keys/parameters, while JavaScript resolves display text.

Integration checklist

  • Reject malformed structures before publishing a loaded form.
  • Populate all input values before rule evaluation and validation.
  • Apply dependent work after user edits and source results.
  • Preserve stable repeater identities across reorder and rollback failed structure changes.
  • Ignore stale asynchronous results and recover state continuity explicitly.
  • Keep validation keys, credentials, localization, and meaningful values in their documented layers.
  • Retain edits across language changes and editor view switches.