feat(spec,lint): the ui vocabularies admit what the renderers implement (objectui#2945) - #4070
Merged
Merged
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckThis PR changes 2 package(s): 105 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
…nt (objectui#2945)
Additions-only follow-up to the vocabulary audit. Nothing narrows, so no
already-stored metadata changes meaning.
- ChartTypeSchema admits `combo`. The taxonomy could not name the one
family the rest of chart.zod.ts is written for: ChartSeriesSchema.type
exists to override a series' type ("combo charts", per its own doc
comment) and ChartSeriesSchema.yAxis binds left/right, which only means
something for mixed marks. objectui draws it distinctly and carried
`combo` in a local fork whose comment claimed to mirror this list.
- WidgetActionTypeSchema is ActionType. They disagreed by `form`, and
backwards: a widget/header action dispatches through the same
ActionRunner that implements `form` (DashboardRenderer routes everything
but a raw `url` into it, deliberately — #3528), so the narrower enum
rejected at validation what the dispatcher executes.
- ListChartConfigSchema.chartType is ChartTypeSchema.extract([...]) —
same five members, a de-duplication rather than a widening.
- @objectstack/lint's chart-family set derives from the taxonomy. Its
omissions failed in the worst direction: an unlisted family read as
"not a chart", so a widget with no chartConfig measure mapping PASSED
validation. Pinning the old hand-written list back reproduces exactly
that for `combo` — zero findings — which is the new test's teeth.
ActionType deliberately does NOT gain `navigation`: executeNavigation is a
strictly weaker executeUrl (no ${param} interpolation, no apiBase
promotion, no openIn) differing only by `replace`, and its one live
producer is the SDUI element:button `action` prop that
ElementButtonPropsSchema does not model at all. Adding the name would put
a second spelling of "navigate" into a closed vocabulary without closing
the real gap.
spec 6944 tests / 267 files and lint 544 / 37 green; tsc clean on both.
Refs objectstack-ai/objectui#2945, objectstack-ai/objectui#2901, #3528
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ce docs regenerate
Two gates fired on the taxonomy addition, both correctly.
- app-showcase's coverage test requires a gallery widget for every
distinctly-renderable ChartType, so admitting `combo` without
demonstrating it failed. The Chart Gallery now shows one: task count as
bars on the left axis, avg progress as a line on the right — the
configuration series[].type / series[].yAxis exist for. Rows below it
shift down by 4 to keep the 12-column layout non-overlapping.
- content/docs/references/ui/{chart,dashboard,report}.mdx regenerated for
the new chart-type member and the two derived enums.
check:docs, check:api-surface and check:authorable-surface pass; the
showcase coverage suite is 13/13.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`view.zod.ts` now imports `chart.zod.ts` (for the extracted list-chart subset), so the taxonomy joins that skill's transitive reference set. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…rt type Fifth generated artefact tracking the spec surface: the react-blocks contract and its reference table inline the ChartType union. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
xuyushun441-sys
force-pushed
the
claude/spec-vocab-2945-additions
branch
from
July 30, 2026 08:36
bd8ffb8 to
f93da17
Compare
check-i18n-coverage freezes each example's untranslated-declared-string count and fails on growth; the `combo` widget's title took app-showcase from 456 to 457. Translated in both locales the example claims to support, which is the remedy the bundle's own `globalActions` comment prescribes. The gallery's other 20 widget titles predate that ratchet and stay part of the frozen baseline — this does not ratchet the number up. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
xuyushun441-sys
pushed a commit
that referenced
this pull request
Jul 30, 2026
…tionSchema (objectui#2997)
element:button's renderer reads `properties.action` and dispatches it
through the ActionRunner — it is the only thing making a standalone-page
button interactive — and ElementButtonPropsSchema did not declare it.
It is authored anyway: cloud's service-tenant pages carry five sites across
the billing and pricing funnel, each `{type:'navigation', to: …}`, each cast
`as any`.
An undeclared prop is STRIPPED, not ignored — safeParse returned success
with no `action` in `data`. Harmless only because page block `properties`
are still z.record(z.string(), z.unknown()); the moment that is tightened
to validate against ComponentPropsMap, every one of those buttons loses its
action at save with a green parse.
InlineActionSchema is derived, not a new dialect. ActionSchema is
z.object(…).refine(…).refine(…), so `.pick()` is unavailable on the export;
the object half is now a factory both build from, preserving lazySchema's
deferral. The inline schema picks the twelve fields element:button actually
forwards, sharing their describe() text, the ActionType vocabulary and the
target-required refinement.
`name`/`label` are optional — a registry entry needs an identity and a menu
label, an inline action has neither (the button supplies its own label).
Registry-only concerns are excluded, as are icon/variant (the button has its
own) and body (a page button running a sandboxed script is a separate
decision).
`navigation` and `to` become normalizing aliases folding to `url`/`target`,
the VIEW_FILTER_OPERATOR_ALIASES pattern: cloud's pages keep validating
while output is always canonical, and the aliases can be tombstoned once
producers migrate. `url` is also the better target — its runner path has
${param} interpolation, apiBase promotion and popup-safe openIn; the
navigation path has none of those.
NOT done: promoting `navigation` into ActionType bare (declined in #4070) —
it names the type while leaving the prop undeclared and `to` homeless, and
adds a permanent synonym to a vocabulary whose members cannot be removed.
Nothing narrowed; all generated surfaces gain entries only. spec suite
7038/271 green, tsc clean, all twelve check:* gates pass.
Refs objectstack-ai/objectui#2997, objectstack-ai/objectui#2945, #4070
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
xuyushun441-sys
pushed a commit
that referenced
this pull request
Jul 30, 2026
…tionSchema (objectui#2997)
element:button's renderer reads `properties.action` and dispatches it
through the ActionRunner — it is the only thing making a standalone-page
button interactive — and ElementButtonPropsSchema did not declare it.
It is authored anyway: cloud's service-tenant pages carry five sites across
the billing and pricing funnel, each `{type:'navigation', to: …}`, each cast
`as any`.
An undeclared prop is STRIPPED, not ignored — safeParse returned success
with no `action` in `data`. Harmless only because page block `properties`
are still z.record(z.string(), z.unknown()); the moment that is tightened
to validate against ComponentPropsMap, every one of those buttons loses its
action at save with a green parse.
InlineActionSchema is derived, not a new dialect. ActionSchema is
z.object(…).refine(…).refine(…), so `.pick()` is unavailable on the export;
the object half is now a factory both build from, preserving lazySchema's
deferral. The inline schema picks the twelve fields element:button actually
forwards, sharing their describe() text, the ActionType vocabulary and the
target-required refinement.
`name`/`label` are optional — a registry entry needs an identity and a menu
label, an inline action has neither (the button supplies its own label).
Registry-only concerns are excluded, as are icon/variant (the button has its
own) and body (a page button running a sandboxed script is a separate
decision).
`navigation` and `to` become normalizing aliases folding to `url`/`target`,
the VIEW_FILTER_OPERATOR_ALIASES pattern: cloud's pages keep validating
while output is always canonical, and the aliases can be tombstoned once
producers migrate. `url` is also the better target — its runner path has
${param} interpolation, apiBase promotion and popup-safe openIn; the
navigation path has none of those.
NOT done: promoting `navigation` into ActionType bare (declined in #4070) —
it names the type while leaving the prop undeclared and `to` homeless, and
adds a permanent synonym to a vocabulary whose members cannot be removed.
Nothing narrowed; all generated surfaces gain entries only. spec suite
7038/271 green, tsc clean, all twelve check:* gates pass.
Refs objectstack-ai/objectui#2997, objectstack-ai/objectui#2945, #4070
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
os-zhuang
added a commit
that referenced
this pull request
Jul 30, 2026
…tionSchema (objectui#2997) (#4135) element:button's renderer reads `properties.action` and dispatches it through the ActionRunner — it is the only thing making a standalone-page button interactive — and ElementButtonPropsSchema did not declare it. It is authored anyway: cloud's service-tenant pages carry five sites across the billing and pricing funnel, each `{type:'navigation', to: …}`, each cast `as any`. An undeclared prop is STRIPPED, not ignored — safeParse returned success with no `action` in `data`. Harmless only because page block `properties` are still z.record(z.string(), z.unknown()); the moment that is tightened to validate against ComponentPropsMap, every one of those buttons loses its action at save with a green parse. InlineActionSchema is derived, not a new dialect. ActionSchema is z.object(…).refine(…).refine(…), so `.pick()` is unavailable on the export; the object half is now a factory both build from, preserving lazySchema's deferral. The inline schema picks the twelve fields element:button actually forwards, sharing their describe() text, the ActionType vocabulary and the target-required refinement. `name`/`label` are optional — a registry entry needs an identity and a menu label, an inline action has neither (the button supplies its own label). Registry-only concerns are excluded, as are icon/variant (the button has its own) and body (a page button running a sandboxed script is a separate decision). `navigation` and `to` become normalizing aliases folding to `url`/`target`, the VIEW_FILTER_OPERATOR_ALIASES pattern: cloud's pages keep validating while output is always canonical, and the aliases can be tombstoned once producers migrate. `url` is also the better target — its runner path has ${param} interpolation, apiBase promotion and popup-safe openIn; the navigation path has none of those. NOT done: promoting `navigation` into ActionType bare (declined in #4070) — it names the type while leaving the prop undeclared and `to` homeless, and adds a permanent synonym to a vocabulary whose members cannot be removed. Nothing narrowed; all generated surfaces gain entries only. spec suite 7038/271 green, tsc clean, all twelve check:* gates pass. Refs objectstack-ai/objectui#2997, objectstack-ai/objectui#2945, #4070 Co-authored-by: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Additions-only follow-up to the vocabulary audit (objectstack-ai/objectui#2901, objectstack-ai/objectui#2945). Companion to #4061, which deletes the orphans; this one closes the gaps in the opposite direction.
Nothing here narrows a vocabulary. No accepted value stops being accepted, so no already-stored metadata and no in-flight payload changes meaning. That is why this is safe while the actual consolidation stays blocked on #3948.
ChartTypeSchemaadmitscomboThe taxonomy could not name the one chart family the rest of
chart.zod.tsis written for:ChartSeriesSchema.typeoverrides a series' chart type — its own doc comment says "combo charts";ChartSeriesSchema.yAxisbinds a series to the left or right axis, which is only meaningful for mixed marks.objectui's renderer draws it distinctly — mixed bar/line/area, dual axes, per-series type resolution (
AdvancedChartImpl.tsx:819) — and had to carrycomboin a local fork of this list, in a set whose comment claims to be "every value the spec'sChartTypeSchemaadmits".This satisfies the taxonomy's own admission rule (in the
NOTEbelow the enum): only families the default renderer draws distinctly.combodoes; it is not a variant that falls back to a base family.WidgetActionTypeSchemaisActionTypeThe two lists had drifted apart by one member,
form, and the disagreement was backwards.A dashboard header or widget action button dispatches through the same
ActionRunnerthat implementsform. objectui'sDashboardRendererroutes everything except a rawurlinto it, deliberately — the code comment records that the previousmodal/scriptallow-list meant aflowheader action fell through to a warn and never dispatched (#3528).So the narrower enum rejected at validation exactly what the shared dispatcher then executes at runtime. Derived rather than restated: the next type the runner implements needs one edit, not two.
dashboard.zod.ts→action.zod.tsis a new import edge with no cycle (action.zod.tsimports neitherdashboard.zod.tsnor anything that reaches it).ListChartConfigSchema.chartTypeisChartTypeSchema.extract([...])Same five members as before — a de-duplication, not a widening. A member renamed in the chart taxonomy now fails at build time instead of leaving a second list quietly disagreeing with the first. (
.extract()resolves correctly through thelazySchemaproxy — verified by execution, and by the guard.)view.zod.ts→chart.zod.tsis likewise cycle-free (chart.zod.tsimports onlyi18n.zodandlazy-schema).@objectstack/lint's chart-family set derives from the taxonomyvalidate-widget-bindingsdecides which widgets need achartConfigmeasure mapping from a hand-written list of families. Its omissions fail in the worst direction: an unlisted family reads as "not a chart", so a widget missing its measure mapping passes validation instead of being reported.combowas exactly that case. Proven rather than asserted — pinning the old hand-written list back:The set is now the taxonomy minus an explicit
MEASURE_EXEMPT_CHART_TYPES(single-value: gauge/solid-gauge/metric/kpi/bullet; tabular: table/pivot), so a family added to the spec is covered without editing the rule.Guards
packages/spec/src/ui/vocabulary-derivation.test.tsasserts both derivations still hold — a restated list fails silently, since it keeps validating, just not what the other list says:donutandcombo, accepts each of the five, and defaults tobar;WidgetActionTypeSchema's options equalActionType's exactly,formparses, an unknown type still throws.The lint suite now walks every multi-series family in the taxonomy rather than a list of its own.
One item from the audit deliberately not done
The audit also proposed promoting
navigationintoActionType. I did not, and the reason is the audit's own thesis:ActionRunner.executeNavigationis a strictly weakerexecuteUrl— no${param.X}/${ctx.X}interpolation, noapiBasepromotion for/api/...paths, noopenIn— differing only by areplaceoption;element:buttonactionprop, andElementButtonPropsSchemadoes not model anactionprop at all (page blockpropertiesarez.record(z.string(), z.unknown()), so it is neither validated nor stripped today).Promoting the name would add a second spelling of navigate to a closed authorable vocabulary — members cannot be removed later, per the audit's own rule — without closing the gap that actually exists, which is the undeclared
element:button.action. Worth its own issue; not worth a one-way door here.Verification
@objectstack/spec: 6944 tests across 267 files, green.tsc --noEmitclean.@objectstack/lint: 544 tests across 37 files, green.tsc --noEmitclean for the touched file (two remaining diagnostics in this worktree are@objectstack/formulahaving no localdisttypes, in files this PR does not touch).Refs objectstack-ai/objectui#2945, objectstack-ai/objectui#2901, #3528, #4061
🤖 Generated with Claude Code