Skip to content

[Decision] Is an authored handler EXPRESSION a supported dialect? 56 mirrors say function, 11 say string, and both appear in files that mirror the other #6182

Description

@yinlianghui-tw

Escalated by the domain:devx @ objectui execution seat (#5748), PM session session_019b5UBNMtTzKbVtZZGvFuxe, from #6152's measurement. ⛔ This cannot be read off the code — it is a product question about the authoring contract, and the seat is not guessing it.

The measurement

A census of every on* key mirrored in packages/types/src/zod/ found 73 mirrored handler keys in two incompatible dialects:

dialectcount
z.function()56
z.string() — a handler expression11 (onClick, onInstall, onPreview, onSave, onCancel, onBack, onViewChange, onChange)
z.any()3
named schema3

⚠️Both dialects appear in files that also mirror the other, so this is not a clean per-file or per-surface split that could be inferred. Two answers coexist in one codebase with nothing declaring which is intended.

Why it needs a person

The two dialects make incompatible promises to an author:

  • z.function()cannot be satisfied by any serialized authored document. A JSON or YAML schema can never produce a function, so the key is declared-but-unauthorable — it exists for a runtime caller, not for a metadata author.
  • z.string() says the opposite: an author may write a handler as an expression, and something resolves it.

objectui#4453 already narrowed one such key to typeof === 'function', so an authored string handler is silently DROPPED (packages/plugin-calendar/src/calendar-view-renderer.tsx:322-331). If that is the intended direction, the 11 z.string() mirrors are advertising a capability the runtime discards — the "declared but not enforced" shape this whole family exists to close, pointing the other way.

What it decides

It changes the remedy for a lane already in flight.#6152 ruled that 23 callback-shaped declared-but-unmirrored keys are runtime-only and non-authorable (reclassified, not mirrored). That ruling is safe under either answer here — but:

  • If z.function() is intended, the 11 z.string() mirrors are legacy to retire, and the 23 stay runtime-only. Clean.
  • If the expression dialect is intended, then the remedy for those 23 is not "reclassify" but ⭐ "offer an authored expression key beside each runtime callback" — a materially larger and different piece of work.
  • If both are intended for different surfaces (blocks/app author expressions; component schemas carry runtime callbacks), then the rule needs stating somewhere a tool can read, because today nothing does.

⚠️#5155's standing ruling places rejection at the zod/publish boundary. That makes these mirrors the authoritative statement of what an author may write — which is exactly why two contradictory statements in that layer matter more than they would in prose.

What is NOT being asked

⛔ Not asking anyone to pick a favourite. Not proposing a sweep. ⛔ #6152's option A is not blocked by this and should land on its own — the 23 keys are runtime slots today under either answer, and adding authored-expression keys later is additive rather than contradictory.

The question is only: which dialect is the contract, so the next card in this family stops re-deriving it from a 56-to-11 vote.

Reproduce

rg -n 'on[A-Z][A-Za-z]*:\s*z\.(function|string|any)' packages/types/src/zod/

Refs: #6152 (where it was measured, and the lane it changes) · #6058 / PR #6149 (the ledger that surfaced it) · #4453 (an authored string handler is dropped) · #5155 (rejection lives at the zod/publish boundary) · #6122 / PR #6142 (no renderer reads 16 schema-level event slots).

Metadata

Metadata

Assignees

No one assigned

    Labels

    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpm:blocked

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions