Problem
The form contract YAML uses keywords like data-table, side-nav, field-array, etc., but nothing machine-readable defines what those keywords mean, what they render, or what properties each requires. The vocabulary is currently implicit across three places:
- TypeScript unions in
types.ts — define what values are valid but not what they do
contract-reference.ts — human-readable docs, but it's a string blob embedded in code, not parseable
FormRenderer.tsx — the actual behavior, buried in implementation
An adapter author today would have to reverse-engineer the renderer to understand what display: data-table requires (columns config, source property) or what component: field-array expects (sub-fields, min_items/max_items).
Proposal
Create a formal contract schema as a JSON Schema written in YAML (e.g. contract.schema.yaml). Since the entire contract system is YAML-based, keeping the schema in YAML maintains consistency. JSON Schema is format-agnostic — tools like ajv can validate YAML documents against a schema loaded from .yaml.
Example sketch:
# contract.schema.yaml
type: object
required: [form]
properties:
form:
type: object
required: [id, title, schema, layout, pages]
properties:
id:
type: string
pattern: "^[a-z][a-z0-9-]*$"
description: Unique form identifier (kebab-case)
title:
type: string
description: Display title shown in the form header
layout:
type: object
required: [navigation, display]
properties:
navigation:
enum: [step-indicator, side-nav, in-page, top-nav, none]
description: How users move between pages
display:
enum: [paginated, scrollable, accordion, split-panel, data-table]
description: How page content is arranged
pages:
type: array
items:
$ref: '#/$defs/Page'
$defs:
Page:
type: object
required: [id, title]
properties:
id:
type: string
display:
description: Per-page display override
enum: [paginated, scrollable, accordion, split-panel, data-table]
fields:
type: array
items:
$ref: '#/$defs/Field'
Field:
type: object
required: [ref, component]
properties:
ref:
type: string
description: Dot-path into the data model
component:
enum: [text-input, date-input, radio, select, checkbox-group, field-array]
width:
enum: [full, half, third, two-thirds]
# ... conditional rules for field-array requiring sub-fields, etc.
What this enables
- Validation — tooling can validate contracts against the schema before runtime
- Adapter authoring — implementers can understand the full surface area without reading TypeScript
- Documentation — generate human-readable docs from the schema instead of maintaining
contract-reference.ts by hand
- IDE support — YAML editors can provide autocomplete and inline error checking via the
$schema directive
Starting point
The contract-reference.ts content and types.ts unions already capture much of this — the work is formalizing them into a machine-readable YAML-based JSON Schema. Conditional requirements (e.g., data-table display requires columns) can use JSON Schema's if/then keywords.
Moved from codeforamerica/safety-net-blueprint#81
Problem
The form contract YAML uses keywords like
data-table,side-nav,field-array, etc., but nothing machine-readable defines what those keywords mean, what they render, or what properties each requires. The vocabulary is currently implicit across three places:types.ts— define what values are valid but not what they docontract-reference.ts— human-readable docs, but it's a string blob embedded in code, not parseableFormRenderer.tsx— the actual behavior, buried in implementationAn adapter author today would have to reverse-engineer the renderer to understand what
display: data-tablerequires (columnsconfig,sourceproperty) or whatcomponent: field-arrayexpects (sub-fields,min_items/max_items).Proposal
Create a formal contract schema as a JSON Schema written in YAML (e.g.
contract.schema.yaml). Since the entire contract system is YAML-based, keeping the schema in YAML maintains consistency. JSON Schema is format-agnostic — tools likeajvcan validate YAML documents against a schema loaded from.yaml.Example sketch:
What this enables
contract-reference.tsby hand$schemadirectiveStarting point
The
contract-reference.tscontent andtypes.tsunions already capture much of this — the work is formalizing them into a machine-readable YAML-based JSON Schema. Conditional requirements (e.g.,data-tabledisplay requirescolumns) can use JSON Schema'sif/thenkeywords.Moved from codeforamerica/safety-net-blueprint#81