Agent docs

16 · A module settings form

You were asked for a settings page, a module's parameters, a wizard step that configures a run, a…

You were asked for a settings page, a module's parameters, a wizard step that configures a run, a "form for the X module". This is what that request brings along.

Audience: an agent (or person) asked to build the settings surface of a module, a run configuration, or any form that a schema could describe.

Read the bundle first. It is the completeness contract: every row is something the request implies whether or not anyone said it. The rule that decides the whole recipe is row 1: if a JSON Schema in the kit's subset can describe the form, the form is SchemaForm over that schema and the rest of the bundle is already handled; if it cannot, the schema is the thing to fix (add the field shape to the subset through the spec), never a hand-built second anatomy.

Traits involved: 13 forms and fields, 05 cards and surfaces, 04 loading and states, 09 page composition, 02 buttons, 10 overlays, 11 motion.

The bundle#

#Comes alongDefaultLawDeviate when
1The form itselfSchemaForm over the module's params schema (packages/ui/docs/schema-form.md); the schema is the source of the fields, the widths, the sections and the messages13 §A1, 13 §A11a one-off field with no schema anywhere composes the same anatomy by hand from Input and Select; never a second anatomy
2Schema conformancelintSchema(schema) returns ok in a test; the manifest lint runs it in CI13 §Bno; a schema outside the subset is refused by name at mount, never rendered with a fallback
3Field widthsderived from the type (numerics and short enums a third, segmented of three or four a half, text full); x-width for the exception13 §A2never full-width numerics
4SectionsCard per x-section, declared order, advanced collapsed with the changed count13 §A3a form of five or fewer fields has no sections
5Branching choicex-widget: mode-chooser cards over a discriminated oneOf; branch defaults on switch, the other branch pruned at submit13 §A4a flat three-way choice with no field consequences is segmented
6Repeating rowsx-widget: table for short rows, list for rich rows; add at the bottom, duplicate and remove trailing, cards under sm13 §A5rows seeded from the file (x-seed-from) hide add and remove
7Viewer tiertier prop from the session; x-tier fields hidden below their tier and chipped admin or team above customer13 §A1never a second gate in the page around a field the schema already tiers
8Dependent fieldsx-when on a sibling or $settings.<key>; hidden values dropped from submit()'s return13 §A8no
9Options from datax-options-from and x-suggestions-from through one resolveOptions(path, params); a loading line, a Retry fallback, pending while a required option list loads13 §A7, 04 §Astatic enums stay in the schema
10Context from the filecontext map for x-seed-from, x-hint-from, x-variables-from (columns, samples, distinct values)13 §Cno
11Validation timingblur once touched, live after an error, all on submit, first error focused13 §A7no
12Message voiceplain sentences from the keyword table; never the schema description as an error13 §A7no
13Server checksvalidate(value) returning path-keyed issues rendered in place; the form is pending while it runs13 §A7a form with no server semantics passes none
14Warningsx-warn, suggestion misses and the app's status line warn and never block13 §A7no
15The action barthe host's: a primary button reading isValid, pending, isDirty, disabled only when it can explain itself; submit() returns the pruned value or null13 §A8, 02 §A4no; the form never renders Save
16Presetsthe app's picker (a template row, a Select) plus presets and activePreset; a Reset to template calling resetToPreset(); dirty measured against the active preset13 §A10a module with no templates passes none
17The asidethe module's preview or summary through aside, beside the form at 1280 and below it at 375; it runs against unsaved state and says so13 §Ca module with nothing to preview has no aside
18Review stepa wizard's last step is mode="review" with onEditSection returning to the field; the job page shows mode="snapshot" over the frozen value13 §A6no
19Loadingloading for the shape-matched skeleton while the schema or the value loads13 §A9, 04 §A2never a spinner
20Both themesexercised in versable-dark and versable-light at 1280 and 375 before done14no

Worked example: the four V1 console modules#

Each module is a committed schema under docs/plan/65-schema-form-fixtures/ and a pattern page that renders it with the console's slots stubbed: /patterns/module-content-generation (rows 4, 5, 6, 16, 17, 18), /patterns/module-image-generation (rows 5, 14, 17), /patterns/module-attribute-normalization (rows 6, 9, 10), /patterns/module-part-type-matching (rows 3, 7, 10). The component page /components/schema-form shows every widget alone.

Do-nots#

The forms trait's list (docs/design-language/13-forms-and-fields.md), in the recipe's voice, plus the one that belongs to the recipe alone.

Do not put a native select where a mode has behavioural weight. A choice that changes which fields exist is a row of cards.

Do not mark a required field with a dot or a bare asterisk. The mark is the kit's RequiredMark, a red asterisk with a "Required" tooltip, centred on the label row; optional fields say nothing.

Do not stretch a numeric field to full width. Numbers and short enums are a third; the widths are the rhythm.

Do not show a spinner as the form's loading state. The skeleton is the form's own shape, labels as themselves and controls as bars.

Do not reuse the schema's description as an error message. Errors are plain sentences from the keyword table.

Do not fall back silently when a schema is outside the subset. The form refuses by name; the schema is what gets fixed.

Do not put layout keys in a schema. x-width and x-section are the whole allowance; pixels, columns and colours belong to the kit.

Do not build a second field anatomy beside the kit's. A hand-composed field copies the shape, or the shape is added to the canon first.

Do not render a Save button inside the form. The action bar is the host's, and its button reads the form's validity.

@versable-git/ui · reference, canon, and method, read in place