Blocked-by: #7344
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.
RULED 2026-08-25 (maintainer, batch adjudication, verbatim 「同意」): Option A — the handler-expression string dialect is NOT a supported authoring form. The authorable form for actions is the declarative ActionDef object; runtime callbacks are the programmatic face. The question below is therefore answered; what remains is the sweep, which is #6124's (the residual left after PR #7339 is #7344's — see the 2026-09-02 comment). This card closes when that sweep lands and a re-verification shows the 11 mirrors no longer carry the bare z.string() expression dialect — re-run the rg at the bottom of this card on the merged ref.
The measurement
A census of every on* key mirrored in packages/types/src/zod/ found 73 mirrored handler keys in two incompatible dialects:
| dialect |
count |
z.function() |
56 |
z.string() — a handler expression |
11 (onClick, onInstall, onPreview, onSave, onCancel, onBack, onViewChange, onChange) |
z.any() |
3 |
| named schema |
3 |
⚠️ 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 needed 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). The ruling confirms that 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 decided
⭐ 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). Under the ruling as issued:
⚠️ #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 mattered more than they would in prose.
What is NOT being asked
⛔ Not asking anyone to pick a favourite. Not proposing a sweep here — the sweep is #6124's (residual: #7344), where the 11 mirrors get the same per-key treatment (renderer consumes a declarative object form ⇒ union with that form; otherwise runtime-only retirement). ⛔ #6152's option A is not blocked by this and should land on its own.
Reproduce (also the close condition — run it on the merged ref)
rg -n 'on[A-Z][A-Za-z]*:\s*z\.(function|string|any)' packages/types/src/zod/
Refs: #6124 / PR #7339 (the function-site sweep, landed 2026-09-02) · #7344 (the residual sweep this card is blocked on) · #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).
Blocked-by: #7344
Escalated by the
domain:devx@ objectui execution seat (#5748), PM sessionsession_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 inpackages/types/src/zod/found 73 mirrored handler keys in two incompatible dialects:z.function()z.string()— a handler expressiononClick,onInstall,onPreview,onSave,onCancel,onBack,onViewChange,onChange)z.any()Why it needed 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). The ruling confirms that direction: the 11z.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 decided
⭐ 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). Under the ruling as issued:
z.function()is the contract ⇒ the 11z.string()mirrors are legacy to retire, and 121 declared-but-unmirrored keys across 16 schema pairs — the lane #6058's new UnmirroredDeclared ledger made visible #6152's 23 keys stay runtime-only, its ruling unchanged.z.function(), which NO JSON document can satisfy — 14 in-repo corpus files author them and are silently invalid #6124 ruling comment), so no future card re-derives the dialect from a 56-to-11 vote.What is NOT being asked
⛔ Not asking anyone to pick a favourite. Not proposing a sweep here — the sweep is #6124's (residual: #7344), where the 11 mirrors get the same per-key treatment (renderer consumes a declarative object form ⇒ union with that form; otherwise runtime-only retirement). ⛔ #6152's option A is not blocked by this and should land on its own.
Reproduce (also the close condition — run it on the merged ref)
Refs: #6124 / PR #7339 (the function-site sweep, landed 2026-09-02) · #7344 (the residual sweep this card is blocked on) · #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).