@veil-runtime/ui 0.4.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,139 @@
1
+ # Interactive Tables — v0.5.0 development candidate
2
+
3
+ Tables may opt into stable rows and host-governed actions. Lists remain scalar,
4
+ noninteractive collections. This increment adds no sorting, filtering, pagination,
5
+ selection, business logic, network execution or mutation engine.
6
+
7
+ ```json
8
+ {
9
+ "id": "orders-screen",
10
+ "actions": ["order.open"],
11
+ "data": { "orders": [{ "id": "123", "number": "SO-123", "status": "Pending" }] },
12
+ "screen": {
13
+ "key": "orders", "component": "Table", "ariaLabel": "Orders",
14
+ "binding": { "source": "data", "path": "orders" },
15
+ "rowKey": "id",
16
+ "columns": [
17
+ { "key": "number", "label": "Order" },
18
+ { "key": "status", "label": "Status", "presentation": "badge" }
19
+ ],
20
+ "rowActions": [{ "action": "order.open", "label": "Open order" }]
21
+ }
22
+ }
23
+ ```
24
+
25
+ The host must supply `allowedActions: ['order.open']` when creating this controller
26
+ or mounted interface. Row IDs are **opaque lookup references**. Neither validation,
27
+ allowlisting, labels, row data nor model-generated definitions authorize access to
28
+ a record. The trusted host must resolve the actual record, check the current
29
+ principal's permissions and execute the operation under its own governance.
30
+ Never turn arbitrary event action strings into backend operation names.
31
+
32
+ Public additions to `/interface`:
33
+
34
+ ```ts
35
+ type InterfaceCellPresentation = 'text' | 'badge';
36
+ interface InterfaceColumn {
37
+ key: string; label: string; presentation?: InterfaceCellPresentation;
38
+ }
39
+ interface InterfaceRowAction { action: string; label: string; }
40
+ interface InterfaceTableOptions { rowKey?: string; rowActions?: InterfaceRowAction[]; }
41
+ // InterfaceNode and the built-in Table branch include InterfaceTableOptions.
42
+ // InterfaceEvent additionally has rowId?: string.
43
+ // InterfaceController additionally has:
44
+ dispatchRow(action: string, source: string, rowId: string, expectedRevision: number): boolean;
45
+ ```
46
+
47
+ `rowKey` names a direct own property, using the existing identifier grammar and
48
+ excluding reserved keys. Dots in this property name are literal, not traversal.
49
+ It may appear without actions. Every bound row must then contain a string matching
50
+ `^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$`, unique within that Table. There is no coercion.
51
+ Missing, invalid or duplicate IDs reject the whole candidate, including data and
52
+ definition replacements. An undefined binding displays an empty collection;
53
+ null and non-array bindings remain invalid. Legacy Tables need no IDs.
54
+
55
+ `rowActions`, when present, requires `rowKey`, explicit host `allowedActions`, and
56
+ 1–8 exact `{action,label}` entries. Action identifiers must be declared in
57
+ `definition.actions` and unique within the Table; labels must be nonblank strings
58
+ of at most 256 characters. All declared definition actions must be host-allowed.
59
+ The controller snapshots the host allowlist. Replace the controller to change
60
+ host policy; definition replacement cannot widen it.
61
+
62
+ Column `presentation` accepts only `text` (default) and `badge`. Both display the
63
+ existing scalar cell value, with null/missing values empty. The badge is a neutral
64
+ bordered span, uses no inferred colors or workflow meanings, and has no live-region
65
+ role. No HTML, CSS, template, URL, callback or arbitrary formatter is accepted.
66
+ Other unknown definition properties remain rejected.
67
+
68
+ Existing bounds remain: 100 rows, 20 data columns, 512 interface nodes, 15 child
69
+ edges, and aggregate depth 32 / 10,000 values / 256 keys / 1,000 array items /
70
+ 65,536-character strings / conservative 1 MiB accounting. A generated Actions
71
+ column adds at most 800 native buttons per Table. Hosts must cap raw bytes before
72
+ parsing; aggregate bounds may reject definitions below individual maxima.
73
+
74
+ An accepted request emits this shape (with an isolated state copy):
75
+
76
+ ```json
77
+ { "action": "order.open", "source": "orders", "rowId": "123", "state": {}, "revision": 7 }
78
+ ```
79
+
80
+ It contains no row object, index or arbitrary parameters. Button event shapes and
81
+ `dispatch(action, source)` remain unchanged; that method cannot emit Table actions.
82
+ `dispatchRow` validates arguments, checks the exact expected controller revision,
83
+ current visibility through ancestors, declared and host-allowed Table action,
84
+ and current row membership. Malformed arguments or unavailable actions throw.
85
+ Stale revisions, missing rows, disposal and synchronous reentrant dispatch return
86
+ false. Dispatch during a render transaction throws. True means event delivery was
87
+ accepted, not that record authorization or execution succeeded. Host errors and
88
+ rejected promises use existing `onError` handling.
89
+
90
+ Pointerdown and Enter/Space keydown capture the rendered revision. A commit before
91
+ the native click is delivered makes the gesture stale. Enter normally clicks at
92
+ keydown; Space at keyup. Keyboard repeat does not generate repeated requests.
93
+ Click-only activation, including assistive-technology and programmatic clicks,
94
+ captures the rendered revision on the Table's click capture phase and delivers on
95
+ its bubble phase, rejecting target-handler updates between those points. Host
96
+ updates before capture precede activation; updates after delivery cannot retract
97
+ an already emitted request. Reusing the same click Event does not dispatch twice;
98
+ separate fresh clicks are separate requests. There is no asynchronous operation
99
+ lock or deduplication of host operations: the host owns idempotency and cancellation.
100
+
101
+ Rows reconcile within interface ID + Table key + binding source/path + rowKey +
102
+ row ID. Action controls reconcile by action ID. Reorders and replacement data
103
+ retain surviving controls and focus; changing identity scope replaces them.
104
+ Removed/disposed controls are inert. Removing the focused row does not focus a
105
+ different record automatically. Invalid candidates do not advance revision.
106
+ Renderer preparation and cleanup retain the existing transaction lifecycle.
107
+
108
+ Native table headers and buttons expose accessible structure. Button names include
109
+ the visible label and opaque ID, e.g. `Open order, 123`. Tab/Shift+Tab follow DOM
110
+ row/action order; Enter/Space activate. There is no grid role or arrow-key mode.
111
+ Labels should describe the operation clearly; opaque IDs may be less readable
112
+ than record names. Automated accessibility assertions cover semantics and names;
113
+ manual screen-reader certification is outside these checks.
114
+
115
+ Hosts can use `updateData(result, event.revision)` to discard late results. Existing
116
+ controller disposal does not cancel host network operations; the host retains its
117
+ AbortController and teardown responsibilities. See
118
+ [`collections.mjs`](../examples/universal/collections.mjs) for an injected host example.
119
+
120
+ Manifest v3 exposes the fields, bounds, presentation choices, identity scope,
121
+ authorization and dispatch behavior. Exact-version manifest consumers must adapt.
122
+ Existing v0.4.2 Table/List/Button definitions remain valid. Custom objects implementing
123
+ InterfaceController must add dispatchRow; strict event consumers must allow rowId
124
+ on row events. Published v0.4.2 artifacts are unaffected.
125
+
126
+ Verification commands from repository root:
127
+
128
+ ```sh
129
+ npm --workspace @veil-runtime/ui run check
130
+ npm --workspace @veil-runtime/ui run test:browser
131
+ VEIL_PLAYWRIGHT_MODULE=/path/to/playwright/index.mjs npm --workspace @veil-runtime/ui run test:browser:collections
132
+ npm --workspace @veil-runtime/ui run verify:consumer
133
+ git diff --check
134
+ ```
135
+
136
+ The collection browser script uses a host-installed Playwright module (or the
137
+ `playwright` package available to Node), system Google Chrome, and Playwright's
138
+ Firefox/WebKit installations. `BROWSER=chromium|firefox|webkit` selects one engine;
139
+ without it all three run. It bundles the current source, not published artifacts.
@@ -0,0 +1,105 @@
1
+ # Interface capability metadata — 0.5.0 development candidate
2
+
3
+ `getInterfaceCapabilities()` now returns manifest version 5. Version 2 introduced the
4
+ metadata described below; version 3 adds
5
+ [Interactive Tables](INTERACTIVE-COLLECTIONS-0.5.0.md); version 4 adds
6
+ [Richer Forms](RICHER-FORMS-0.5.0.md); version 5 adds
7
+ [Host-driven asynchronous presentation](ASYNC-PRESENTATION-0.5.0.md). Existing component vocabulary
8
+ and metadata fields remain available. New `bindings`, `limits`, and `behavior` fields give
9
+ machine-readable details for producer tooling. The published
10
+ `validateInterfaceDefinition` implementation remains the acceptance authority;
11
+ metadata is a description, not a second validator or host authorization policy.
12
+
13
+ ## Precise limits
14
+
15
+ The tree has at most 512 nodes and **15 child edges** from `screen` to a leaf. The
16
+ v0.4.2 manifest advertised 16, but the generic bounded-data traversal ran first:
17
+ each `children` array consumes another level, so a 16-edge chain was already
18
+ rejected. This candidate changes the explicit tree bound to 15; it does not make
19
+ any formerly rejected tree acceptable. The aggregate traversal still permits
20
+ depth 32, 10,000 visited values, 256 own keys per object, 1,000 array items,
21
+ 65,536 characters per string, and a conservative 1 MiB accounting budget.
22
+ Nested data or conditions can make a shallower tree fail the aggregate bound.
23
+ Hosts must cap raw bytes before JSON parsing.
24
+
25
+ Conditions retain depth 8, 64 expression nodes, and 1–16 terms in each `all` or
26
+ `any`. Tables retain 100 rows and 20 columns; Lists retain 100 scalar items;
27
+ Select retains 100 options. The returned `limits` and `behavior` objects expose
28
+ these bounds without relaxing them.
29
+
30
+ ## Compatibility and producer integration
31
+
32
+ Before the Interactive Tables increment, the `InterfaceDefinition` JSON shape,
33
+ TypeScript node types, event fields, and controller APIs were unchanged from v0.4.2.
34
+ The optional collection additions are documented in the linked specification.
35
+ Increment 1 changed the manifest from version 1 to 2 and its `treeDepth` value from
36
+ 16 to 15; consumers requiring exact manifest v1 must adapt. Interactive Tables
37
+ advance it to version 3; Richer Forms advances it to version 4; asynchronous presentation advances it to version 5. Exact-version
38
+ consumers must adapt. Form events and controller APIs are unchanged. The validator's acceptance set is unchanged at the depth
39
+ boundary, because aggregate traversal already rejected 16 child edges. Binding
40
+ paths with an empty interior segment remain accepted by the existing pattern;
41
+ this is described explicitly rather than tightened during this increment.
42
+
43
+ In a future showcase integration, the producer-contract generator should branch
44
+ on the current manifest version, derive its structural schema from `components`, `bindings`,
45
+ and `limits`, and derive behavioral guidance from `behavior`. It should keep the
46
+ host's action, path, component, and resource projection separate. Its v0.4.2
47
+ compatibility supplement should remain pinned to that exact published artifact;
48
+ new conformance tests should compare any reduced supplement with this validator.
49
+ Never treat a generated schema as a replacement for UI validation or host policy.
50
+
51
+ ## Responsive Grid increment
52
+
53
+ `Grid` is a built-in container alongside `Stack` and `Row`. Its JSON shape is:
54
+
55
+ ```json
56
+ {
57
+ "key": "summary",
58
+ "component": "Grid",
59
+ "columns": 1,
60
+ "columnsAt": { "sm": 2, "lg": 4 },
61
+ "gap": "md",
62
+ "children": []
63
+ }
64
+ ```
65
+
66
+ `columns` is the mobile count and defaults to 1. `columnsAt.sm` takes effect at
67
+ 600 CSS pixels; `columnsAt.lg` takes effect at 1024 CSS pixels. Omitted breakpoint
68
+ counts inherit the last defined count. Each count must be an integer from 1 to 4.
69
+ Tracks are equal `minmax(0, 1fr)` columns. `gap` defaults to `md`; the complete
70
+ allowlist is `none` (0), `sm` (0.5rem), `md` (0.75rem), and `lg` (1.5rem).
71
+ `columnsAt` accepts only `sm` and `lg`. The validator rejects all other values,
72
+ CSS properties, placement, and order controls. Alignment is left to the existing
73
+ stretch behavior to keep this contract small. The renderer installs a finite
74
+ stylesheet for the breakpoint classes and removes it on unmount.
75
+
76
+ Children remain in JSON and DOM order at every width. Tab order follows that DOM
77
+ order. A column change only changes track count; Grid does not place children in
78
+ named areas or reorder them. Keyed definition updates retain compatible children,
79
+ including focused inputs, when changing Grid properties or sibling order. Grid
80
+ can nest inside Stack and Row and vice versa. Consumers that enumerate built-ins
81
+ or reject unknown manifest component names must add Grid; existing definitions
82
+ continue to validate. `Table.columns` remains its existing column descriptor
83
+ array; `Grid.columns` is a numeric track count. The manifest describes both.
84
+ The broad `InterfaceNode.columns` TypeScript property now includes both shapes;
85
+ code reading Table columns from that broad type should narrow with
86
+ `Array.isArray(node.columns)`. `BuiltinInterfaceDefinition` keeps each component's
87
+ shape specific.
88
+
89
+ Grid uses viewport breakpoints rather than container queries. Small host
90
+ containers inside a wide viewport may still receive multiple tracks; hosts
91
+ should allow sufficient width or use a one-column definition. Grid has no spans,
92
+ named areas, masonry, custom alignment, or arbitrary CSS. Host CSP must permit
93
+ the renderer's finite style element, as it does other renderer-set styles.
94
+
95
+ ## Asynchronous presentation metadata (v5)
96
+
97
+ `Status.properties` adds `phase`; `propertyTypes.phase` and
98
+ `behavior.presentation` describe literal/bound phases, strict message validation,
99
+ default messages, polite atomic semantics, programmatic focus recovery and
100
+ host-owned retry/orchestration. A phased Status bound message is a string of at
101
+ most `limits.textLength` UTF-16 units, including when hidden. Legacy Status keeps
102
+ its prior text binding behavior. No component, event or controller API is added.
103
+ Existing conditions select content; existing allowed Buttons request host actions;
104
+ existing `expectedRevision` rejects stale results. See the dedicated contract for
105
+ focus ordering, host responsibilities and the full revision protocol.
@@ -0,0 +1,47 @@
1
+ # Veil UI v0.5.0 release notes and migration
2
+
3
+ Unpublished Developer Preview candidate. Local qualification does not authorize npm publication, tagging, pushing or deployment. The source-directory publication hold remains active. Consult [qualification evidence](RELEASE-QUALIFICATION-0.5.0.md) before approving an exact tarball.
4
+
5
+ ## New capabilities
6
+
7
+ - Capability manifest v5 describes aggregate bounds, effective tree depth and behavior. The runtime validator remains authoritative.
8
+ - Responsive Grid supports 1–4 equal columns, viewport breakpoints at 600px and 1024px, four finite gaps, keyed children and stable DOM/Tab order.
9
+ - Tables optionally support stable row IDs, neutral scalar badges and explicitly host-allowlisted row actions. Events include an opaque `rowId`; `dispatchRow` requires an exact revision. Native pointer/keyboard gestures reject intervening commits.
10
+ - Input, Textarea and Select gain contextual descriptions and conditional disabling. Text/number Input and Textarea gain read-only flags. Help/errors have accessible associations; validating Button activation focuses the first invalid eligible field.
11
+ - Status optionally supports loading, ready, empty and error phases, strict bounded messages, safe defaults, polite atomic semantics and focus recovery after removal, hiding or disabling. Retry uses an ordinary Button and host callback; there is no execution engine.
12
+
13
+ The five feature contracts are capability metadata and Grid in [INTERFACE-CAPABILITIES-0.5.0.md](INTERFACE-CAPABILITIES-0.5.0.md), [collections](INTERACTIVE-COLLECTIONS-0.5.0.md), [forms](RICHER-FORMS-0.5.0.md), and [asynchronous presentation](ASYNC-PRESENTATION-0.5.0.md). Intermediate manifest v2/v3/v4 references explain increment history; consumers receive v5.
14
+
15
+ ## Compatibility changes requiring migration
16
+
17
+ Existing v0.4.2 JSON definitions remain supported. There are no removed public exports or export paths. This is not universally source-compatible for TypeScript implementers:
18
+
19
+ 1. Exact manifest-v1 consumers must explicitly accept **v5**, add Grid to built-in enumerations, and support the optional properties. Derive structural guidance from `components`, `propertyTypes`, `bindings` and `limits`, and behavioral guidance from `behavior`. Keep host policy separate and validate actual definitions with UI. The manifest is descriptive metadata, not a complete JSON Schema or authorization policy. Common node `key` and `component` are always required even though component-specific `required` arrays list only additional properties.
20
+ 2. Custom `InterfaceController` implementations must add `dispatchRow(action, source, rowId, expectedRevision): boolean`. Validate arguments, reject stale/disposed/reentrant requests, require explicit host allowlisting, current visible Table/action and row membership, and emit an isolated state copy. Follow the [exact collection contract](INTERACTIVE-COLLECTIONS-0.5.0.md). Controllers with no row support may reject row dispatch as unavailable; never forward it blindly to host execution. The native DOM adapter requires the supplied controller's internal transactional bridge; a structural custom implementation needs its own adapter consuming snapshots/subscriptions. Do not import private dist paths.
21
+ 3. `InterfaceNode.columns` is now `InterfaceColumn[] | GridColumns | undefined`. Narrow broad-node reads with `Array.isArray(node.columns)`. `BuiltinInterfaceDefinition` provides component-specific shapes.
22
+ 4. Strict event-shape consumers must allow optional `rowId` on row events. Ordinary Button event fields and `dispatch(action, source)` remain unchanged. Buttons cannot emit Table actions.
23
+ 5. The advertised tree depth changes from 16 to **15 child edges**. Aggregate traversal already rejected 16-edge trees in v0.4.2; this corrects metadata rather than tightening the previous acceptance boundary. Depth 32, 10,000 visited values, 256 object keys, 1,000 array items, 65,536-unit strings and conservative 1 MiB aggregate budgets can reject shallower definitions. Limit raw bytes before decoding. Legacy binding grammar, including empty interior path segments, is retained.
24
+
25
+ New fields are opt-in. Legacy Status keeps its prior message behavior. Only phased Status requires supplied bound text to resolve to a string of at most 4096 UTF-16 units, even when hidden. Hidden/disabled fields retain values in event state; read-only fields still validate. Native number inputs normalize incomplete spelling. Eager validation and the five existing rules remain unchanged.
26
+
27
+ ## Installation and ownership
28
+
29
+ Use the qualified local artifact until separately authorized publication:
30
+
31
+ ```sh
32
+ npm install /absolute/path/veil-runtime-ui-0.5.0.tgz
33
+ ```
34
+
35
+ Public ESM paths are `@veil-runtime/ui`, `/inspection`, `/composition`, `/interface`, and `/dom`. Direct CommonJS require and private dist imports are unsupported. Server-side minimum Node is 24; the supported TypeScript baseline is 6.0.2 with NodeNext. Bundle `/dom` for browsers. There is one runtime dependency, `zod: ^4.6.6`; this range is not an immutable transitive lock. `@veil-runtime/core: ^0.4.0` is optional, qualified explicitly at 0.4.0. `/interface`, `/composition` and `/dom` work without Core; strict legacy root type consumers need Core's declarations. No support for Core 0.5/0.6 is claimed.
36
+
37
+ Always supply `allowedActions` for untrusted definitions. Legacy Buttons permit omission for compatibility; omission is not deny-by-default. Row actions require an explicit allowlist. The controller snapshots that policy; replace the controller to change it. Authorize the actual record in the host, validate server inputs again, and never interpret arbitrary action strings as executable operation names.
38
+
39
+ Use `createMountedInterface().dispose()` for owned teardown. With the lower-level API, call both the mount disposer and `controller.dispose()`. Controller disposal alone does not remove another owner's DOM. Trusted component factories must use their abort signal and dispose resources. They are ordinary trusted JavaScript, not sandboxed extensions.
40
+
41
+ Pass `expectedRevision` on asynchronous completions, captured after accepting loading. Every accepted edit/update invalidates that snapshot-wide token; the host decides whether to discard or rebase. Disposal and revision guards suppress UI updates, not external effects. Hosts own cancellation, idempotency, business authorization, public message selection and persistence.
42
+
43
+ ## Qualification fixes and limits
44
+
45
+ Qualification updates current guides and package/lock metadata from the increment's `0.5.0-dev.0` identity to `0.5.0`. It introduces no runtime feature or API changes. Historical release documents and the published v0.4.2 artifact remain untouched.
46
+
47
+ Automated tests cover Chromium, Firefox and WebKit; they do not certify screen readers, live announcement timing, zoom/high-contrast modes or host themes. Heading remains h2; host CSS/CSP and trusted extensions affect accessibility. Focus recovery chooses the first visible phased Status and does not return focus automatically. There is no sorting, pagination, interactive List, rich HTML/URL cell, implicit submission, async validation, request orchestration or automatic retry.
@@ -0,0 +1,157 @@
1
+ # Veil UI v0.5.0 independent release qualification
2
+
3
+ Date: 2026-11-14. Decision: **GO for the bounded Developer Preview package**, with the limitations below. No unresolved P0/P1 defect was found after qualification corrections. Publication requires a separate authorization of the exact final tarball; this audit performs no publish, push, merge, tag or deployment.
4
+
5
+ ## 1. Git baseline and candidate
6
+
7
+ - Repository: `/home/mustapha/dev/ai/veil-ui-v050`.
8
+ - Verified branch: `feature/ui-v0.5-contract-metadata`.
9
+ - Required and observed starting HEAD: `f66ff6efbff4b86046f49bf648415f00f242b687`.
10
+ - Starting `git status --porcelain=v1`: empty.
11
+ - v0.4.2 compatibility reference: local tag commit `b1b5a3ade4be929674872cbc6c81d3a49aa1ede7`. This was read using `git archive`; historical files/artifacts were not changed.
12
+ - Audited package/code candidate: `09e5093b0b92886ae8a3fbc020699bb0580db13e` (release metadata, current guides and migration checkpoint). Runtime source is byte-identical to the starting HEAD.
13
+ - The qualification checkpoint is the documentation-only child of that candidate containing this report. Its exact SHA is recorded by `git log -1 --format=%H` and the final delivery message, avoiding a self-referential commit hash. Final packaging includes this report and is repeated after its addition.
14
+
15
+ Only package release metadata, package-lock workspace version and current UI documentation were changed. Core and the separate showcase were not modified. Repository tests changed the laboratory `data/operator.db`; those test-owned bytes were restored exactly from starting HEAD and are not checkpoint inputs. Generated dist, example bundles, logs and tarballs are excluded from commits.
16
+
17
+ ## 2. Frozen release scope
18
+
19
+ Read the handover, package configuration, manifest, public entry points, controller/renderer/validation implementation, examples and test infrastructure. The five approved increments occupy four contract files: metadata and Grid share `INTERFACE-CAPABILITIES-0.5.0.md`; the others are `INTERACTIVE-COLLECTIONS-0.5.0.md`, `RICHER-FORMS-0.5.0.md` and `ASYNC-PRESENTATION-0.5.0.md`.
20
+
21
+ | Increment | Qualified behavior |
22
+ |---|---|
23
+ | Metadata | Manifest v5; aggregate and component bounds; effective 15-edge tree depth; producer guidance |
24
+ | Grid | 1–4 equal tracks; 600/1024px viewport breakpoints; finite gaps; stable DOM/keyboard order |
25
+ | Collections | Optional keyed Table rows; scalar text/badge cells; explicit host-allowed row actions; revision/gesture checks |
26
+ | Forms | Descriptions; conditional disabled fields; readOnly text/number controls; accessible eager feedback and invalid-submit focus |
27
+ | Async presentation | Optional four-phase Status; bounded public messages/defaults; polite atomic semantics; focus recovery; host-owned retry/results |
28
+
29
+ All twelve built-ins were reviewed: Stack, Row, Grid, Text, Heading, Status, Input, Textarea, Select, Button, Table and List. The separate composition API retains Stack/Form/Field/Submit/Text/Status. Root capability graph, rich input/output, deterministic presentation, interactions, registry, workflow descriptions and inspection remain supported; optional provider callbacks remain experimental trusted host code. No new capability was added during qualification.
30
+
31
+ ## 3. Public API, manifest and compatibility
32
+
33
+ Compiler export inventory compared all five public TypeScript entry points against v0.4.2: root **144**, inspection **3**, composition **11**, interface **29**, DOM **8** symbols. There are **no removed symbols**. Seven new types are GridColumns, GridGap, GridBreakpoints, InterfaceCellPresentation, InterfaceRowAction, InterfaceTableOptions and InterfacePresentationPhase. All other source changes since v0.4.2 are confined to interface.ts, dom.ts and the shared bounded-limit metadata export. Declarations are checked with `skipLibCheck: false` in isolated consumers.
34
+
35
+ Independent differential probes accepted **39/39** generated legacy definitions in both versions: each old component/input mode with absent, equality and compound visibility. This is finite evidence, not an exhaustive proof for every possible JSON value. Source comparison and legacy regressions support the compatibility conclusion. v0.4.2 definition behavior remains supported; no previous rejection boundary was relaxed.
36
+
37
+ Breaking/source compatibility changes are explicitly separated from additive JSON features in [release notes and migration](RELEASE-0.5.0.md): exact manifest version 1 becomes 5; built-in enumerations gain Grid; custom controller implementers need required dispatchRow; broad InterfaceNode.columns readers must narrow its widened numeric/array union; strict event parsers must permit rowId. The advertised tree-depth correction from 16 to 15 does not reject previously accepted 16-edge trees: aggregate depth already rejected them.
38
+
39
+ The manifest uses the validator's property lists and shared limits. Independent cross-component probes rejected **178/178** unsupported property combinations. Tests check aggregate depth/node/key/item/string/estimated-byte limits, condition budgets, collection identities/actions, Grid values, form rules and phased messages, including rejected updates preserving accepted revisions. Metadata is descriptive, not a complete schema: common key/component are mandatory beyond component-specific `required`; runtime binding validity and host policy require execution of the actual validator/controller checks. Missing generic display bindings produce empty text, but supplied phased Status bindings must resolve correctly, including while hidden. Text length 4096 applies to literal interface text and phased messages; legacy bound display values remain subject to the larger aggregate budget.
40
+
41
+ Public typing helpers (`defineInterface`, schema definition helpers) return their argument; they do not validate untrusted input. Broad InterfaceNode intentionally supports trusted registered components. Condition/binding/validation helpers consume validated snapshots/contracts; they are not replacements for whole-definition validation. Strict built-in static types and runtime validation serve different purposes.
42
+
43
+ Custom structural controllers alone cannot use native mountInterface: the adapter requires the private transaction bridge installed by createInterfaceController. This pre-existing integration restriction is now explicit. Use the supplied controller or a separate snapshot/subscription renderer; private dist imports are unsupported.
44
+
45
+ ## 4. Security and governance
46
+
47
+ Reviewed path: bounded bytes owned by host → parsed definition → descriptor-safe bounded-data validation → exact built-in/component validation → copied controller definition/data/state → candidate binding validation → renderer preparation → accepted revision/DOM → eligibility-checked dispatch → isolated event → trusted host handler → host authorization/external execution.
48
+
49
+ | Boundary | Evidence and conclusion |
50
+ |---|---|
51
+ | Data/prototype safety | Descriptor inspection rejects accessors before invocation, reserved keys, cycles, nonplain records, symbols, sparse/extra-property arrays and unsupported values. Own-property lookup prevents inherited binding traversal. Independent create/update probes observed zero getter invocations and no pollution. Existing security tests cover recursive input/output and provider isolation. |
52
+ | Resource limits | Depth 32, 10,000 values, 256 keys, 1,000 items, 65,536-unit strings, conservative 1 MiB accounting; interface max 512 nodes/15 edges; conditions depth 8/64 nodes/16 terms; Tables 100 rows/20 columns/8 actions, Lists 100, Select 100. Definition, runtime data and state copies are independently bounded. Rejected candidates do not advance revision. |
53
+ | Host actions | Explicit allowedActions is copied, checked on creation and replacement, and cannot be widened through definition changes. Row actions require it; legacy Buttons permit omission. Untrusted-definition hosts must pass an explicit allowlist. Declaration/visibility/disabled checks do not authorize business effects. |
54
+ | Row boundary | dispatchRow checks arguments, expected revision, visibility through ancestors, Table/action policy and exact current row membership. Only opaque rowId is emitted, without record/index/parameters. Host must re-resolve and authorize the actual record. Ordinary dispatch cannot emit Table actions. |
55
+ | Stale/reentrant dispatch | Stale row gestures and results, missing rows and disposed/reentrant row delivery return false; malformed/unavailable row requests throw. Ordinary synchronous reentrant dispatch is ignored. Pointer/key/click propagation races and repeated Event suppression pass three engines. Fresh later activations remain separate requests. Ordinary Buttons check current eligibility without a row-style gesture revision lock. |
56
+ | Async ownership | Optional expectedRevision rejects late results; any accepted edit/update invalidates the token. Event promise failures route to onError, with no automatic message rendering. Disposal suppresses later UI updates; host effects are not cancelled. No operation scheduler, retry engine or deduplication is introduced. |
57
+ | Text and errors | Veil-owned renderer uses textContent/text-node updates and finite DOM/style properties; no raw HTML/template/URL execution path. Injection probes and regressions pass. Phased messages reject objects and oversized/nonstring bindings. Diagnostics can contain supplied identifiers/provider error text and are not public-safe error codes; host must map/redact them. Legacy bound text may stringify plain bounded objects. |
58
+ | Lifecycle/transactions | Renderer preparation failure preserves accepted controller state; staged custom instances are discarded and aborted. Owned teardown removes listeners, rows, styles and subscriptions. Raw controller disposal and a separately owned mount have distinct lifetimes; both must be disposed. Commit guarantees assume normal ownership of DOM. |
59
+ | Trusted extensions | Factory/provider/handler JavaScript is trusted, receives isolated metadata but is not sandboxed. A factory throwing before returning can be signalled to abort but cannot have an unavailable disposer called. Host must avoid external effects during preparation and attach cleanup immediately. |
60
+
61
+ **Confirmed within the declarative built-in boundary:** definitions cannot independently initiate network requests, mutate databases, execute arbitrary code or select unauthorized host operations when an explicit host allowlist and authoritative host handler are used. Source inspection found no fetch/XHR/WebSocket/eval/Function/execution import in the shipped declarative runtime. Core imports are type-only. Composition invokes only separately registered handlers; experimental providers receive an inert capability executor. Built-in elements accept no arbitrary HTML, script, URL or CSS property. This does not constrain arbitrary JavaScript already running in the host, malicious proxies/intrinsics, trusted extensions, compromised dependencies or an incorrectly authorized callback. It is not an application sandbox or a complete security proof.
62
+
63
+ Host responsibilities remain raw-byte/time/concurrency limits, explicit action policy, principal/record authorization, validated public DTOs, server-side business validation, cancellation, idempotency, stale-result handling and safe public messages. Hidden/disabled/readOnly flags and presentation phase are not confidentiality controls; event state includes hidden/disabled values. Bounded input does not promise low latency for every maximum-size composed DOM; hosts should project smaller budgets for their environment.
64
+
65
+ ## 5. Runtime, browser and accessibility evidence
66
+
67
+ Fresh engines: **Chromium 151.0.7922.108, Firefox 157.0, WebKit 27.2**.
68
+
69
+ - Original composition/universal suites pass Chromium: mobile/desktop/195px layout, accessibility tree, labels, field validation, Enter submission, success/failure, reduced-motion behavior, keyed updates, focus/selection, escaping and remount/disposal.
70
+ - Collections pass all three: native table/header/button semantics and accessible names, Tab/Shift+Tab and Enter/Space, row/action identity and reorder retention, pointer/key/click races, duplicate Event suppression, safe cells and inert removed controls.
71
+ - Forms pass all three: text/number/checkbox/Input, Textarea and Select; label/help/error associations, eager validation, first-invalid focus, keyboard submission, disabled/readOnly/current-definition event guards, hidden ancestors, compatible state/selection retention and disposal.
72
+ - Async presentation passes all three: loading/ready/empty/error and defaults, polite atomic status, keyboard retry through host action, focused-control retention and recovery for removal/hiding/disabling, hidden destination exclusion, rapid/out-of-order results, intervening edits, rejection atomicity, escaping and composition with Table/List/Grid/forms.
73
+ - Text stability passes all three: retained/changed text nodes, nested keyed reordering, rejected updates and **10/10 blur-driven mouse actions per engine**.
74
+ - Additional independent Grid probes pass all three at widths **390, 599, 600, 1023, 1024 and 1440**: expected 1/2/4 tracks, consistent Tab order, escaped hostile text, Status defaults and owned listener/style teardown.
75
+ - Source backend Chromium HTTP example passes success, failure, retry and overlapping result suppression. Its server is outside the npm package.
76
+
77
+ Manual screen-reader announcement timing, NVDA/JAWS/VoiceOver combinations, magnification/200–400% zoom, forced colors/high contrast, touch/virtual keyboards and arbitrary host themes/extensions were **not manually verified**. Automated accessible structure is evidence, not WCAG certification. Fixed h2 hierarchy, first-visible phased Status focus destination, no implicit submission, native number spelling normalization and host CSP/style requirements remain documented limits. Older browsers and framework adapters are unqualified.
78
+
79
+ ## 6. Distribution and independent consumers
80
+
81
+ Fresh-source qualifier installs from package-lock with a new npm cache and no reused node_modules, runs build/typechecks/tests, packs twice, checks tar contents/legal files, installs a Core consumer, compiles strict declarations, runs standalone policy denial/success, bundles for browsers, checks resolution/ESM boundaries, audits runtime graphs and dry-runs the exact tarball publication. Source-directory publish dry-run is expected to exit 1 with the publication-hold error; exact-tarball dry-run passes without publishing.
82
+
83
+ The package is `@veil-runtime/ui@0.5.0`, Apache-2.0, Node >=24, ESM only. The package and lockfile agree. The five export maps point to existing JS/declaration pairs. There are 54 verified emitted files plus the build fingerprint manifest. No dangling source maps, source/cache/node_modules/database/env files or lab server are packed. Documentation, legal files and standalone/composed/universal examples are included. Core is an optional ^0.4.0 peer; published 0.4.0 integration passes strict types and runtime without changing Core. Higher major/minor Core lines are outside the declared peer range and not claimed.
84
+
85
+ Separate installed TypeScript 6.0.2 and JavaScript projects pass **without installing Core**. Only Zod is a direct runtime dependency. Tests exercise informational/form/structured data, Grid, collections, richer forms, asynchronous transitions/stale completion, JSON acceptance, event updates and disposal. Browser bundle metadata excludes Core runtime. Direct CommonJS require fails as intended; undocumented deep imports are unavailable through exports.
86
+
87
+ Fresh independent UI runtime and UI+Core consumer npm production audits report **0 vulnerabilities** at qualification time. The root laboratory production audit reports **5** (3 moderate, 1 high, 1 critical) in its separate dependency graph. Those lab dependencies are excluded from UI's tarball/runtime graph; this decision does not qualify the lab or its deployment. Dependency ranges may resolve differently later and must be audited again at publication.
88
+
89
+ Final artifact/evidence directory: `/tmp/veil-v050-release-qualification-final/`; authoritative tarball SHA-256, byte count and entry inventory are in `qualification.json`, `commands.json` and `artifacts/`. The final delivery records the hash. Repeat packaging must have identical hashes. These local logs are temporary; this report preserves commands and observed results, not every raw log byte.
90
+
91
+ ## 7. Commands and outcomes
92
+
93
+ All commands ran from repository root unless noted. No test was suppressed or validation weakened.
94
+
95
+ | Command | Outcome |
96
+ |---|---|
97
+ | `npm --workspace @veil-runtime/ui run check` | PASS: production build, test/source typecheck, **110/110 UI tests**, zero skips, 54-file dist integrity and source/dist/cycle boundary checks |
98
+ | `npm test` | PASS: **110 UI + 93 registry/domain tests**, zero failures/skips |
99
+ | `npm run typecheck:ui-integration` | PASS |
100
+ | `npm --workspace @veil-runtime/ui run test:browser` | PASS: original composition and universal Chromium suites |
101
+ | `VEIL_PLAYWRIGHT_MODULE=/tmp/veil-v050-publication-main/node_modules/playwright/index.mjs npm --workspace @veil-runtime/ui run test:browser:collections` | PASS: Chromium/Firefox/WebKit |
102
+ | Same module setting, `test:browser:forms` | PASS: Chromium/Firefox/WebKit |
103
+ | Same module setting, `test:browser:async` | PASS: Chromium/Firefox/WebKit |
104
+ | Same module setting, `node packages/ui/test/browser-text-stability.mjs` | PASS: Chromium/Firefox/WebKit |
105
+ | `node packages/ui/test/browser-backend.mjs` | PASS: Chromium local HTTP integration |
106
+ | `node --import tsx /tmp/veil-v050-contract-audit.mjs` | PASS: five-entry export comparison, 39 legacy definitions, 178 property rejection cases, zero accessor invocations/pollution |
107
+ | `node /tmp/veil-v050-layout-audit.mjs` | PASS: independent six-width three-browser Grid, text and teardown probes |
108
+ | `npm --workspace @veil-runtime/ui run verify:consumer` | PASS: repeat packs identical; installed strict TS and JS without Core |
109
+ | `node tools/qualify-ui.mjs --output /tmp/veil-v050-release-qualification-final` | PASS: fresh lockfile install, supported TS baseline, all engineering/packaging/Core/license/audit/publication dry-run gates |
110
+ | `node packages/ui/scripts/verify-universal-consumer.mjs /tmp/veil-v050-release-qualification-final/artifacts/veil-runtime-ui-0.5.0.tgz` | PASS: both no-Core consumers against final exact tarball |
111
+ | `git diff --check` and staged diff review | PASS |
112
+
113
+ Initial sandbox executions failed with spawnSync npm EPERM, Chrome startup failure and tsx IPC listen EPERM. Authorized host execution passed; these were environmental failures, not ignored test failures. Existing checkout node_modules contained TypeScript **7.0.2**, despite the pinned 6.0.2 manifest/lock. The fresh-source and installed-consumer runs explicitly used **6.0.2**; results do not rely on the preinstalled compiler. Node was **24.18.1**. Browser checks use host Playwright/binaries, not a declared package runtime dependency.
114
+
115
+ ## 8. Defect register
116
+
117
+ | ID | Severity | Finding | Disposition |
118
+ |---|---|---|---|
119
+ | RQ-01 | P1 | Package/lock still named 0.5.0-dev.0, unsuitable as the requested exact v0.5.0 artifact | Fixed to 0.5.0 locally; publication hold retained; repack/requalify |
120
+ | RQ-02 | P1 | Current guides advertised v0.3/v0.4.1, omitted built-ins, advertised depth 16 and obsolete browser limits; migration scattered | Fixed README/API/universal guide, release notes and handover pointer; controller/columns/event/manifest migration consolidated |
121
+ | RQ-03 | P2 | Manual assistive-technology/accessibility certification absent | Explicitly recorded; follow-up on host combinations; no certification claim |
122
+ | RQ-04 | P2 | Native DOM adapter's supplied-controller/private-bridge restriction and split controller/mount disposal could be misunderstood | Documented explicitly; no incompatible architectural redesign |
123
+ | RQ-05 | P2 | Existing checkout tool installation differs from lock (TS 7.0.2 vs 6.0.2) | Fresh-source/independent consumer gates establish supported baseline; local node_modules not committed |
124
+ | RQ-06 | P2 | Separate root lab production graph has five audit findings | Reported; excluded from package; no Core/showcase/lab dependency changes |
125
+ | RQ-07 | P3 | Maximum permitted repeated collections can create a large DOM; no performance SLA/benchmark | Host-specific smaller projection budgets recommended; future performance characterization |
126
+
127
+ No P0 was demonstrated. No runtime P1 correction or architecture/API redesign was needed. Qualification changed release metadata and documentation only.
128
+
129
+ ## 9. Release decision and publication handoff
130
+
131
+ **GO** for publication of this exact v0.5.0 Developer Preview candidate after separately authorized artifact review. The advertised bounded feature set, governance, compatibility migration, three-browser regressions and independent installed-consumer behavior are supported by fresh evidence. P0/P1 findings are resolved. Manual accessibility gaps, host authorization/cancellation responsibilities, experimental callbacks and laboratory findings remain explicit limitations rather than implied guarantees.
132
+
133
+ Use [RELEASE-0.5.0.md](RELEASE-0.5.0.md) as release notes and migration guidance. Preserve the exact final tarball and its SHA-256 from the final qualifier. Publishing a modified/rebuilt artifact, changing dependency resolution, removing the source hold, expanding scope or claiming unsupported Core/browser/assistive-technology compatibility requires new review. This report does not authorize publication itself.
134
+
135
+ ## Appendix: complete public TypeScript symbol inventory
136
+
137
+ The following inventory was produced with the supported TypeScript compiler's module export enumeration, including types and re-exports. Each name was compared with v0.4.2; all old names remain. Public declarations, module boundaries and installed resolution were verified as described above. Internal validation/transaction helpers are not exported entry points.
138
+
139
+ ### root (144)
140
+
141
+ `ActionHandler`, `ActionHandlers`, `ActionResult`, `AggregateCapabilityOutputSchema`, `ApplicationExecutionRequest`, `ApplicationExperienceAction`, `ApplicationExperienceCapability`, `ApplicationExperienceRole`, `ApplicationInvocationContext`, `ApplicationManifest`, `ApplicationValidationIssue`, `ApplicationValidationIssueCode`, `ApplicationValidationResult`, `ApplicationValidationSeverity`, `ApplicationWorkflowDefinition`, `ApplicationWorkflowState`, `ApplicationWorkflowTransition`, `AuthoritativeCapabilityMetadata`, `AuthoritativeInputBinding`, `AuthoritativeInputBindings`, `BuiltinInterfaceDefinition`, `BuiltinInterfaceNode`, `CapabilityCondition`, `CapabilityInteractionMode`, `CapabilityInteractionResolution`, `CapabilityMetadataResolver`, `CapabilityOutputSchema`, `CapabilityPresentationProvider`, `CapabilityPresentationRequest`, `CapabilityPresentationSuggestion`, `CapabilityRelationship`, `CapabilityTransition`, `CapabilityView`, `ComponentNode`, `ComposedApplication`, `ComposedSurface`, `CompositionController`, `CompositionSnapshot`, `DeriveCapabilityViewOptions`, `DerivedApplication`, `DerivedApplicationCapability`, `DerivedOutputView`, `DeviceContext`, `GridBreakpoints`, `GridColumns`, `GridGap`, `InteractionMode`, `InterfaceBinding`, `InterfaceCellPresentation`, `InterfaceColumn`, `InterfaceCondition`, `InterfaceConditionExpression`, `InterfaceController`, `InterfaceDefinition`, `InterfaceEvent`, `InterfaceNode`, `InterfaceOption`, `InterfacePresentationPhase`, `InterfaceRowAction`, `InterfaceSnapshot`, `InterfaceTableOptions`, `InterfaceValidation`, `InterfaceValue`, `InterpretableRelationship`, `OutputFieldDefinition`, `PresentationEnvelope`, `PresentationEnvelopeSection`, `PresentationGroup`, `PresentationPlan`, `PresentationProvenance`, `PresentationSuggestionCache`, `PresentationSurface`, `RelatedSurfaceExecution`, `RelationshipInteraction`, `RelationshipRole`, `ResolvedCapabilityInput`, `RichCapabilityInputField`, `RichCapabilityInputSchema`, `RichStringFormat`, `SubmissionStatus`, `SurfaceExecution`, `UIAction`, `UIAggregateMetric`, `UIConfirmation`, `UIDetailField`, `UIField`, `UIFieldType`, `UIRegistry`, `UIRegistryDefinitions`, `UISection`, `UISuccessTransition`, `UITable`, `UITableColumn`, `UIViewDerivation`, `UIViewProvenance`, `ViewContext`, `buildCapabilityExecutionPayload`, `buildPresentationEnvelope`, `composePresentation`, `createCompositionController`, `createInterfaceController`, `createUIRegistry`, `defineApplication`, `defineComposedApplication`, `defineInputSchema`, `defineInterface`, `defineOutputSchema`, `defineRelationships`, `defineTransitions`, `defineWorkflow`, `deriveApplicationActions`, `deriveApplicationExperience`, `deriveCapabilityView`, `deriveDeterministicCapabilityView`, `deriveOutputView`, `deriveUIField`, `deriveUIFields`, `deterministicPresentation`, `evaluateInterfaceCondition`, `formatSemanticValue`, `getInterfaceCapabilities`, `getInterfaceValidation`, `getOutputSchemaFields`, `interpretRelationship`, `isAggregateOutputSchema`, `isExplicitAction`, `isInterfaceField`, `parseApplicationExecutionRequest`, `presentationPlanSchema`, `resolveCapabilityInteraction`, `resolveInterfaceBinding`, `sectionId`, `serializeDateTimeLocal`, `validateApplication`, `validateCapabilityOutput`, `validateComposedApplication`, `validateFieldValue`, `validateInterfaceDefinition`, `validateOutputSchema`, `validatePresentationPlan`, `validateRichCapabilityInput`, `validateRichInputField`, `validateRichInputSchema`, `validateRichInputValue`.
142
+
143
+ ### /inspection (3)
144
+
145
+ `CompositionInspection`, `InspectionEdge`, `buildCompositionInspection`.
146
+
147
+ ### /composition (11)
148
+
149
+ `ActionHandler`, `ActionHandlers`, `ActionResult`, `ComponentNode`, `ComposedApplication`, `CompositionController`, `CompositionSnapshot`, `SubmissionStatus`, `createCompositionController`, `defineComposedApplication`, `validateComposedApplication`.
150
+
151
+ ### /interface (29)
152
+
153
+ `BuiltinInterfaceDefinition`, `BuiltinInterfaceNode`, `GridBreakpoints`, `GridColumns`, `GridGap`, `InterfaceBinding`, `InterfaceCellPresentation`, `InterfaceColumn`, `InterfaceCondition`, `InterfaceConditionExpression`, `InterfaceController`, `InterfaceDefinition`, `InterfaceEvent`, `InterfaceNode`, `InterfaceOption`, `InterfacePresentationPhase`, `InterfaceRowAction`, `InterfaceSnapshot`, `InterfaceTableOptions`, `InterfaceValidation`, `InterfaceValue`, `createInterfaceController`, `defineInterface`, `evaluateInterfaceCondition`, `getInterfaceCapabilities`, `getInterfaceValidation`, `isInterfaceField`, `resolveInterfaceBinding`, `validateInterfaceDefinition`.
154
+
155
+ ### /dom (8)
156
+
157
+ `DOMComponent`, `DOMComponentRegistry`, `InterfaceDOMComponent`, `InterfaceDOMComponentInstance`, `InterfaceDOMRegistry`, `createMountedInterface`, `mountComposedApplication`, `mountInterface`.
@@ -0,0 +1,69 @@
1
+ # Richer forms and validation UX
2
+
3
+ This bounded increment extends the existing field, state and validation machinery.
4
+ It adds no component, event, action permission or controller method. Capability
5
+ manifest version is 4; definitions using the previous vocabulary remain accepted.
6
+
7
+ ## Exact contract
8
+
9
+ Input, Textarea and Select accept optional `description`, a plain string of at most
10
+ 4096 UTF-16 code units, rendered as contextual help. Existing required `label`
11
+ (maximum 256 units) remains the accessible name unless the existing ariaLabel override is used. Help and validation feedback have
12
+ stable, instance-scoped IDs referenced by the control's `aria-describedby`.
13
+ Feedback also uses `aria-errormessage` when invalid and the existing polite live
14
+ region. Strings are rendered with textContent, never HTML.
15
+
16
+ All three fields accept optional `disabled: InterfaceConditionExpression`, exactly
17
+ the same bounded condition syntax as Button.disabled. Disabled fields retain state
18
+ but are excluded from validation. Disabled fields cannot write state through DOM
19
+ events. This does not restrict trusted host calls to setState. Conditions apply to
20
+ the field itself; disabled containers are not introduced.
21
+
22
+ Input (text or number, including omitted text type) and Textarea accept optional
23
+ `readOnly: boolean`. Select and checkbox reject readOnly because their native
24
+ controls do not support it. Read-only fields remain focusable, retain state and
25
+ participate in validation; hosts must supply valid values. Both flags may coexist;
26
+ disabled takes precedence for validation and interaction.
27
+
28
+ Validation timing remains eager: errors are derived from every accepted snapshot,
29
+ including initial mount, and recomputed after state/data/definition updates.
30
+ Button.validate=true gates action delivery through the existing validator. A DOM
31
+ activation of such a button focuses the first invalid eligible control in screen
32
+ order and delivers no event. Native Tab/Shift+Tab and Enter/Space on buttons are
33
+ the accessible submission path. There is no implicit Enter submission from fields.
34
+ Host dispatch remains DOM-independent and throws on validation failure.
35
+
36
+ Existing rules are unchanged: required rejects missing/null/empty strings and an
37
+ unchecked required checkbox; whitespace is text. Optional empty fields skip bounds.
38
+ Length uses UTF-16 code units (integer bounds 0–4096); numeric bounds are finite,
39
+ inclusive and only legal on number inputs. Reversed bounds reject definitions.
40
+ Select values must be declared. The first failing rule supplies the fixed message.
41
+ No custom validation expressions, messages or native browser validation engine are
42
+ introduced. Native required reflects the rule; numeric step is `any`, so decimal
43
+ values accepted by the existing validator do not get a native integer step error.
44
+
45
+ Hidden fields, including hidden ancestors, remain excluded from validation; their
46
+ values remain in state and in action state copies. Disabled values likewise remain
47
+ in event state. Neither hidden nor disabled is a confidentiality or authorization
48
+ boundary. DOM event handlers check the latest accepted definition and ancestor
49
+ visibility, so detached, hidden, disabled or read-only fields cannot write through
50
+ stale DOM references. Compatible updates retain existing control identity, focus,
51
+ selection and entered state under the existing identity/default rules. Changing
52
+ help, labels, constraints or field flags does not change identity. Removed controls
53
+ are inert. Hosts own authorization, persistence, cancellation and revision checks.
54
+
55
+ ## Example
56
+
57
+ See `examples/universal/forms.mjs` for a JSON-only operational request definition.
58
+ The host handles its declared submit action; no backend mutation is implied.
59
+
60
+ ## Deferred
61
+
62
+ Form containers, implicit Enter submission, touched/blur timing, async validators,
63
+ custom messages, field groups, new input types and server errors require separate
64
+ contract design. No extra validation state, submission state or form engine was
65
+ introduced. Automated browser coverage is not manual assistive-technology certification.
66
+
67
+ Native number controls still normalize incomplete/invalid edit spelling to their
68
+ DOM value before the scalar state update. There is no separate draft buffer for
69
+ partial number strings. Validation is authoritative for accepted scalar state.