ADR-032: Schema-Driven Template Builder

Prev Next

Context

The template builder needs to scale to 100+ module types and page settings without hand-writing a form per type. The wire contract (templateGetdata.moduleList[]) is consumed by live OTT apps and cannot change. Editors expect one inspector that switches context the way Figma does — page settings when the canvas is selected, module settings when a module is selected.

Decision

Adopt a schema-driven Template Builder with three pillars (see guidelines/template-builder-architecture.md):

  1. Sanity-shaped definitions — pages and modules are authored with defineType + defineField (name / title / type / fields[]). defineBlock / definePage + f.* remain supported for existing call sites.
  2. One settings rendererSettingsRenderer renders any schema-backed panel (page or module). The dual-mode inspector mounts the same engine for both.
  3. Contract adapterscore/adapters/moduleContract.adapter.ts and pageContract.adapter.ts are the only places that map rich attributes to the wire shapes. Unknown modules round-trip via the raw passthrough block.

Working-copy state, selection, and DnD stay in useSingleTemplate / inspector hooks per the existing one-way data flow.

Consequences

Enables

  • Add a page or module by writing one definition file — no inspector UI changes.
  • Same settings UX for pages and modules.
  • Incremental migration of legacy blocks without breaking existing templates.

Restricts

  • Wire moduleList / page field shapes stay frozen.
  • No hand-written per-type settings forms; UI comes from the schema.

Trade-offs

  • Field kinds must cover real cases; bespoke controls are added as new kinds, not one-off forms.
  • Persistence mutations are still being wired — local apply works first.

Notes

  • Extends the registry-driven precedent of ADR-008
  • Invariant: persisted output must stay compatible with the existing wire contract.