Skip to content

docs(architecture): callback props + scoped cells for parent-child (ADR 0017, #518)#617

Merged
cssbruno merged 4 commits into
mainfrom
design/callback-props-scoped-cells-518
Jun 22, 2026
Merged

docs(architecture): callback props + scoped cells for parent-child (ADR 0017, #518)#617
cssbruno merged 4 commits into
mainfrom
design/callback-props-scoped-cells-518

Conversation

@cssbruno

Copy link
Copy Markdown
Owner

Closes #518.

#518 is an [Architecture] / design-direction issue. This PR delivers the deciding ADR 0017.

The decision

Today parent↔child has three overlapping mechanismsemit + g:on, exports + g:on:exports, and g:bind — and they all already lower to the same wire (data-gowdk-parent-on-<event>, a bubbling CustomEvent; see internal/viewrender/component.go + internal/clientrt/assets/island.js). The divergence is purely in the authoring surface.

ADR 0017 collapses them along the action vs state axis:

  • Actions (discrete) → callback props: <TodoItem onDone={Count++} />. Replaces emit + exports-as-events. Lowers to the existing data-gowdk-parent-on-* transport — we unify the authoring surface, not the wire (the same way g:bind desugars today; the issue's "implementation honesty" note).
  • State (continuous) → writable scoped cells: <SearchBox bind:value={@page Query} />, where bind: is sugar over a value prop + a write-back callback prop. Replaces store-sharing, exports-observation, and g:bind. Depends on [Architecture] Unify reactive state under one primitive with a scope axis (state @scope) #517 (scoped cells).

Net: events are callbacks, state is scoped cells — two principled mechanisms instead of three, no new transport, no event bus (#514 stays closed). Consistent with #384 (one bounded-client IR/evaluator).

Why a doc, phased implementation

[Architecture] issue; the state half is blocked on #517 (scoped cells) and both halves are gated on #384. ADR 0017 makes the binding decision and the migration plan now (deprecate emit/exports/g:bind with precise nudges, breaking acceptable in 0.x), and sequences implementation:

Checklist (from #518)

Files

  • docs/engineering/decisions/0017-callback-props-and-scoped-cells.md
  • docs/engineering/decisions/README.md (index; also backfills 0014/0015)
  • CHANGELOG.md

Part of the interactivity-unification epic #520.

…hild (#518)

Record ADR 0017, deciding the parent-child axis of the interactivity epic (#520).

Today three mechanisms (emit + g:on, exports + g:on:exports, g:bind) all lower
to the same data-gowdk-parent-on-* bubbling transport, so the divergence is in
the authoring surface, not the wire. ADR 0017 collapses them along the action vs
state axis:

- Actions (discrete) -> callback props (onEvent={expr}), lowered to the existing
  parent-on transport (authoring surface unified, wire unchanged - the same way
  g:bind desugars today). Replaces emit + exports-as-events.
- State (continuous) -> writable scoped cells; bind: is sugar over value-prop +
  callback-prop. Replaces store-sharing, exports-observation, and g:bind.
  Depends on #517 (scoped cells).

Includes the deprecation/migration plan (precise nudges, no silent aliases) and
stays consistent with #384. Direction is decided; implementation is phased
(action half is #517-independent; state half gated on #517).

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a016d1cce6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

passes behavior:

```gwdk
<TodoItem onDone={Count++} />

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Normalize callback names before reusing parent-on attrs

With the existing parent-on wire, callback prop names that follow this onDone shape need an explicit normalization rule. The current JS runtime derives the event to listen for by slicing attr.name from data-gowdk-parent-on-* (internal/clientrt/assets/island.js:1561-1563), and HTML attribute names are lowercased in the DOM while CustomEvent names are case-sensitive. If Phase 1 maps onDone to a Done callback event, the listener becomes done and the callback never fires; specify lower-case event names or another stable encoding before locking this surface.

Useful? React with 👍 / 👎.

callback prop that writes it back:

```gwdk
<SearchBox bind:value={@page Query} />

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reserve bind: before colliding with prop renames

The proposed bind:value syntax currently collides with component prop renaming: non-g: attributes containing : are parsed as target:source prop aliases (docs/language/components.md:281-284, implemented in componentPropTarget), so <SearchBox bind:value={...}> would be treated as a prop named bind sourced from value, not as a binding directive. Please make the ADR reserve bind:*/change the rename grammar or choose a non-conflicting syntax before implementers add this surface.

Useful? React with 👍 / 👎.

cssbruno added 3 commits June 21, 2026 23:35
Fold in Codex review points:

- Reserve the callback event-name encoding: onCamelCase authoring maps to the
  lower-cased data-gowdk-parent-on-<event> wire and a lower-cased CustomEvent,
  so the DOM's attribute lower-casing cannot desync parent listener and child
  dispatch.
- Reserve bind:* as a directive prefix matched before the target:source prop-
  rename grammar, so bind:value is a binding directive rather than a prop named
  "bind".
@cssbruno cssbruno merged commit 5179da4 into main Jun 22, 2026
16 checks passed
@cssbruno cssbruno deleted the design/callback-props-scoped-cells-518 branch June 22, 2026 02:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Architecture] Collapse parent-child communication into callback props + scoped cells

1 participant