# v1 data-model reactivity, Cell/Derived, RFC-6901 pointers, value-identity cutoff

Source of truth: [ADR-0078](../../../../../../docs/ops/adr/adr-0078-a2ui-runtime-adopts-v1-data-model.md)
(ratified 2026-08-20, gh#1762; phased plan tracked in gh#1784, **closed
2026-08-28, all four phases P1-P4 shipped**). Read the ADR before touching
`packages/gen-ui/a2ui`'s data-model internals or `surface.js`/`renderer.js`
watch semantics: this file is the routing pointer + the shape of the
adopted contract, not a restatement of the ruling. `path-pointer.js` (the
transitional three-walker shim gh#1763 staged) is deleted as of P4; every
call site now uses the vendored `resolvePointer`/`setPointer`/`deletePointer`/
`createDataModel` primitives directly.

## What's decided (shipped, ADR-0078 P1-P4 complete, gh#1784 closed)

The in-repo A2UI runtime (`packages/gen-ui/a2ui`, the 0.9 dialect) **adopts**
the vendored `packages/genui` v1.0 data model internally, `Cell`/`Derived`
reactive primitives, RFC-6901 pointer semantics (`resolvePointer`/
`setPointer`/`deletePointer` with structural sharing), and the
one-memoized-`Derived`-per-pointer read contract, as its own per-surface
data model. This is an internal implementation swap, not a consumer
migration or a wire-grammar change: the dialect's eight message kinds, the
`{path}` binding-prop shape, `updateDataModel`, `HandlerContext.updateModel/
setModel`, `registerController/Handler/Resolver`, and the `<a2ui-root>`
element API are all unchanged. **[amended 2026-08-28, gh#2268/lld-0005,
gh#2212]** Decision item 2's falsifier is narrowed to wire-GRAMMAR diffs
only: it is falsified if any consumer needs a code change, or if
`dialect-schema.source.mjs`'s wire grammar diffs, a bare `$id`/filename
identifier rename in `dialect-schema.source.mjs` no longer falsifies it.
Phased across future cuts, plan/LLD to follow, nothing rides in the cut
this ADR itself was ratified for (item 6).

## The mechanism, once adopted

- **Write path:** every write notifies the root `Cell`; every cached
  `Derived` recomputes on any write, but a `Derived` notifies its own
  subscribers only when the recomputed value differs by `Object.is`
  (`renderer/dist/data-model.d.ts`, R-R10). The perf win is suppressed
  *notification*, not suppressed *evaluation*, structural-sharing writes
  keep untouched subtrees identity-stable, which is what makes the
  `Object.is` cutoff effective.
- **The falsifiable perf contract (ADR-0078 item 3):** a single-path write
  causes `#applyProps` (or its successor) to execute ONLY for bindings whose
  resolved value at their pointer actually changed. Re-application count is
  the gate, not evaluation count.
- **Pointer grammar:** RFC-6901 throughout, `~0`/`~1` escaping honored, no
  empty-segment forgiveness, uniform missing-path → `undefined`. One
  asymmetry survives by design: `resolvePointer`'s READ path treats only
  `""` as whole-document (`"/"` resolves the empty-string key, strict RFC
  6901), while `setPointer`/`deletePointer`'s WRITE/DELETE path treats
  `undefined`, `""`, and `"/"` all as whole-document.
- **Watch semantics:** a subscriber at a pointer fires when the resolved
  value AT that pointer changes, any writer, any write at/above/beneath the
  pointer that alters it; a sibling write never fires it (host-bridge R-H1,
  same identity-cutoff rule as R-R10). This supersedes `surface.js`'s
  prefix-descend rule and `renderer.js`'s re-apply-all-bindings behavior.

## What converged (P1-P4, complete)

`path-pointer.js` (gh#1763) preserved three divergent legacy walkers
(`getByPath`/`setByPath`, `getPath`/`setPath`, `getModelValue`) as a
deliberate staging step; each call site migrated to the vendored semantics
above in turn (P2: `renderer.js`; P3: `surface.js`/`wiring-registry.js`),
and P4 deleted the module once its last call site migrated, no divergent
walker survives (Decision 4). Every migration phase named its own behavior
deltas rather than changing silently (ADR-0078 item 4's falsifier): `/a//b/`
stopped resolving forgivingly, `/name/length` on a string stopped leaking
the primitive's own property, `~0`/`~1` escaping is now honored, and a2ui's
own read/write asymmetry (previously: `getByPath`/`getPath` treated
absent-path/`""`/`"/"` alike, but `setByPath`/`setPath` no-op'd on root
instead of replacing it) converged on the read/write split named above. The
full Δ1-Δ10 delta table lives in
[lld-0001-a2ui-data-model-consumption §Data](../../../../../../docs/ops/lld/lld-0001-a2ui-data-model-consumption.md#data);
the surviving regression proof for the pointer-only deltas is
`packages/gen-ui/a2ui/data-model-pointer-semantics.test.js` (migrated from
`path-pointer.test.js` in P4).

## What stays fixed (don't "fix" these under this ADR)

- The surface-lifecycle contract (ADR-0061, [surface-lifecycle](surface-lifecycle.md)): the state machine, the
  `beginSurfaceUpdate → applyTo → commitSurfaceUpdate/abortSurfaceUpdate`
  host bracket, `data-a2ui-lifecycle` reflection, bubbling surface events, all orthogonal to data-model storage/invalidation and must stay
  byte-identical across adoption phases.
- The dialect/wire surface (ADR-0059's frame) and ADR-0072's wire-format
  flip, both explicitly untouched by this internal swap.
- `record.js`'s bidirectional-overlap store, app-layer, outside this
  package, rides a separate review track (R2), not this ADR.

## Consumption mechanism, decided (build-time copy)

`@adia-ai/a2ui` is a zero-runtime-deps package (ADR-0048 posture).
**[amended 2026-08-29, ADR-0096, gh#2373, recorded in ADR-0078's own
2026-08-29 amendment]**
`packages/genui` is absorbed first-party in-repo source, not a vendored
artifact, `packages/genui/VENDOR.json` and the vendor-and-sync mechanism
are gone (gh#2372 closed; `VENDOR.json` confirmed absent from origin/main).
[lld-0001](../../../../../../docs/ops/lld/lld-0001-a2ui-data-model-consumption.md)
decided a build-time byte-identical copy of
`packages/genui/renderer/dist/data-model.js` (+`.d.ts`) into
`packages/gen-ui/a2ui/`, with a `data-model.provenance.json` sidecar and a
freshness gate in `npm run check`, preserving the zero-deps posture (no new
package dependency, no covert workspace-import). The provenance sidecar now
points at the in-repo source path instead of a `VENDOR.json` sha: it stamps
`source.js`/`source.dts` + `syncedAt` + `contentHash` only, `packages/genui/renderer/dist/data-model.js`, with no vendor-sha field at
all.

## Eval-floor risk

No eval floor movement is *expected* (floors measure retrieval/composition,
not runtime rendering, floor sources are authoritative in
[eval-diagnostics](eval-diagnostics.md)), but rendered-check verify targets
and any eval path that renders through the runtime need per-phase
verification once a phase actually ships. This reference flags the risk;
each landed phase clears or reports it, not this file.
