Skip to content

fix(types,console): retire the page kind jsx from PageNodeSchema.kind; the console sample moves to html (objectui#12464) - #12485

Merged
objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-12464-retire-page-kind-jsx
Oct 15, 2026
Merged

objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-12464-retire-page-kind-jsx

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #12464
Clause-②: yes — the published @object-ui/types PageNodeSchema.kind narrows (drops 'jsx'), levelled minor (objectui levels a breaking change minor, never major)

The objectui layer of objectstack-ai/objectstack#23543 family F5-jsx, which landed in @objectstack/spec as objectstack-ai/objectstack#23638 (c5dd7fce30): spec 18 refuses the page kind jsx and names html.

What changed

  • @object-ui/types, TS face. PageNodeSchema.kind is 'full' | 'slotted' | 'html' | 'react'; its docblock no longer calls jsx an accepted alias. The @ts-expect-error comment in authoring-nodes-11364.test.ts lists the four kinds.
  • @object-ui/types, zod face (premise correction). The card measured "no 'jsx' page kind in packages/types/src/zod/". That holds for the literal text, but the zod PageNodeSchema takes kind from the spec's PageSchema by reference, so on the pinned 18.0.0-next.1 it still admitted jsx. With the TS face narrowed, the zod face was wider than the TS face, and zod-mirror-parity.test.ts failed types type-check with two TS2322 ("layout.zod.ts#PageNodeSchema" and "kind" not assignable to never, from WiderLedgerMismatch and WiderLedgerKeyDrift). The zod face now picks its enum out of the spec's by reference, stripImportedDefaults(SpecPageSchema).shape.kind.unwrap().extract(['full', 'slotted', 'html', 'react']), the way PAGE_KIND_NODE_TYPE already does for type. Against spec main this is the spec's own enum. Against next.1 it refuses jsx at kind. A kind the spec drops fails to compile here. A ledger entry was not used because it cannot be correct on both spec versions: it would be stale on spec main.
  • Console sample. apps/console/src/sdui-jsx-preview.tsx authors kind: 'html'; its header comment names the html tier. The extraction pin in sdui-preview-page-source-query-params.test.ts reads sdui-jsx-preview.tsx:command_center:html.
  • @object-ui/components page renderer: the 'jsx' arm stays (half 2 deferred, below). Narrowing the type made the arm a compile error: TS2367: This comparison appears to be unintentional because the types '"full" | "slotted" | undefined' and '"jsx"' have no overlap (the components declaration build). The renderer now reads kind as the stored wire value, const kind: string | undefined = schema.kind;. That is a widening annotation, not a cast. Its comment says why: a server on a spec built before the retirement can serve a stored jsx row. No jsx to html mapping is added anywhere. Rendering is unchanged.
  • Docs. content/docs/guide/react-pages.md no longer says "jsx" "is still accepted"; it says the alias was retired in spec 18 and to author "html".
  • Changeset .changeset/12464-retire-page-kind-jsx.md: @object-ui/types: minor, @object-ui/console: patch (the console is published: no private field). @object-ui/components gets no entry because its behaviour does not change.

Half 2 (the renderer's 'jsx' arm): deferred

The pin reading on this tree:

  • packages/types and packages/components both resolve @objectstack/spec 18.0.0-next.1 from the lockfile and the installed store. Its dist/ui/index.mjs declares kind: z.enum(["full", "slotted", "html", "react", "jsx"]) and COMPILED_PAGE_KINDS = ["html", "jsx"].
  • npm view @objectstack/spec@next version answers 18.0.0-next.1 (dist-tags next: 18.0.0-next.1, latest: 17.7.0). The published manifest carries no gitHead.

The resolved spec therefore does not carry c5dd7fce30, and the arm stays as the triage requires. The PM splits it into its own card. Pin window: until a next built from c5dd7fce30 or a descendant is resolved, a stored jsx page served by a next.1 runtime still renders as html through that arm.

Verification (head 4cc23c7f7)

  • Closure build turbo run build --filter='@object-ui/console^...' --concurrency=2: 34/34 tasks.
  • type-check for @object-ui/types, @object-ui/components, @object-ui/app-shell and @object-ui/console: exit 0 each.
  • vitest, root form:
    • packages/types/: 383 files, 10335 tests passed.
    • 19 targeted files (the page renderer suites, the console preview and html-tier suites, public-contract, the app-shell pageRequiresStamp double): 220 tests passed.
  • New pin packages/types/src/__tests__/page-kind-jsx-retired-12464.test.ts: Equal on the whole kind union, a @ts-expect-error on kind: 'jsx', and a zod safeParse refused at path kind with html as the control.
  • Reverse verification (after commit, with a trap restore): 'jsx' was re-added to the TS union and to the .extract list. Then tsc -p tsconfig.test.json reported TS2344 (the Equal pin) and TS2578 (the unused @ts-expect-error), and vitest reported 1 failed | 1 passed (the zod pin). Restore proof: git diff HEAD was empty, and both files' hashes matched their HEAD blobs.
  • eslint over the 7 changed source files: 0 errors and 10 warnings, every warning pre-existing (no-explicit-any and exhaustive-deps on untouched lines). The population is the config's **/*.{ts,tsx} block, and the file count comes from --format json. The config enables no type-aware linting (no projectService or project), so these edits cannot change the verdict on any untouched file.
  • Exit 0 for each of check:phantom-deps, self-import, unreferenced-sources, doc-example-readers, handler-key-reads, side-effects-array, element-data-source-declaration, spec-symbols, action-forward-parity, designer-field-key-parity, icon-record-names, i18n-keys, i18n-drift, i18n-designer-parity, control-bytes and new-line-citations. Also exit 0: check-doc-snippet-types.mjs (792 of 792 blocks judged, after its scoped build), check-changeset-fixed.mjs and check-changeset-presence.mjs. The guard check-governed-queue-guard.mjs --test reports NOT GOVERNED.
  • NOT MEASURED, Spec Main Shape Gate locally. Reason: it needs a spec tarball built from objectstack main, which was not built in this container. The check runs on this PR. By construction, on spec main the TS kind equals the spec's four-value enum, so the TS2430 at PageNodeSchema no longer applies, and the zod .extract is the identity there.
  • NOT MEASURED, check:spec-floors. It exits 1 with no-artifact because it needs a whole-workspace build, and it is the known red on main.

Acceptance notes (noted, not filed)

  • packages/app-shell/src/views/metadata-admin/ResourceEditPage.pageRequiresStamp-11357.test.tsx: the server double's compiledRequires reads body.kind !== 'html' && body.kind !== 'jsx', mirroring next.1's COMPILED_PAGE_KINDS. It is test-only and outside this card's file surface. Carrier: the half-2 card.
  • packages/types/src/zod/layout.zod.ts: the comment on the re-attached spec checks names an html / react / jsx page, and html / jsx for checkPageRequiresKind. That describes next.1's checks. Carrier: the half-2 card.
  • packages/components/src/renderers/layout/page.tsx: the comment above getJsxManifest still reads "a kind:'jsx' page carries a constrained JSX source". Carrier: the half-2 card.
  • The sample keeps its file name sdui-jsx-preview.tsx (with sdui-jsx-preview.html, and the names in two console tests) and its page as any. A rename touches files outside this card's surface. Carrier: none.

Generated by Claude Code

… and move the console sample to html (objectui#12464)

@objectstack/spec 18 refuses the page kind jsx (objectstack-ai/objectstack#23638).
PageNodeSchema.kind drops 'jsx'; the console's sdui-jsx-preview sample authors
kind 'html'. The page renderer keeps its 'jsx' arm for stored rows until the
resolved spec carries the retirement; it reads kind as a widened wire value so
that arm still compiles against the narrowed type.

Claude-Session: https://claude.ai/code/session_01XVp19E1MYUTFM3dnkwz8gB
Co-authored-by: Claude <noreply@anthropic.com>
…ind jsx too (objectui#12464)

The zod mirror took `kind` from the spec's PageSchema by reference, so on the
pinned 18.0.0-next.1 it still admitted 'jsx' and was wider than the narrowed
TS face (zod-mirror-parity WiderLedger TS2322). It now picks its enum out of
the spec's by reference with `.extract`, through the stripImportedDefaults
boundary, identical to the spec's enum once the resolved spec carries the
retirement.

Claude-Session: https://claude.ai/code/session_01XVp19E1MYUTFM3dnkwz8gB
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 70 chunks) 3082.0 KB 3119.4 KB
Main entry chunk (gzip) 73.3 KB 350 KB
Entry file index-ByWu1lqV.js —
Status PASS —

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 20.90KB 7.67KB
app-shell (runtime-config.js) 22.59KB 7.89KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.11KB 3.87KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.22KB 10.61KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.40KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.72KB 2.24KB
auth (SocialSignInButtons.js) 9.70KB 3.93KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.62KB 0.83KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 41.19KB 11.12KB
auth (createAuthenticatedFetch.js) 8.54KB 3.46KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.26KB 8.03KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.28KB 2.60KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (commentAuthorship.js) 1.39KB 0.78KB
collaboration (index.js) 1.93KB 0.85KB
collaboration (useCollaborationTranslation.js) 6.50KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 594.75KB 143.42KB
core (index.js) 10.18KB 4.04KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 254.55KB 71.19KB
fields (index.js) 282.89KB 72.11KB
i18n (LocalizationContext.js) 2.92KB 1.42KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.52KB 2.39KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.35KB 12.88KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 36.87KB 9.88KB
i18n (useSafeTranslation.js) 7.14KB 2.92KB
layout (index.js) 40.67KB 11.85KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 15.35KB 5.51KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.82KB 2.38KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.04KB 3.92KB
plugin-calendar (index.js) 54.94KB 16.05KB
plugin-charts (index.js) 84.97KB 23.36KB
plugin-chatbot (index.js) 201.72KB 48.07KB
plugin-dashboard (index.js) 143.88KB 38.88KB
plugin-designer (index.js) 233.95KB 50.08KB
plugin-detail (index.js) 262.16KB 69.28KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 179.38KB 46.59KB
plugin-gantt (index.js) 178.36KB 45.08KB
plugin-grid (index.js) 261.92KB 72.66KB
plugin-kanban (index.js) 53.23KB 16.71KB
plugin-list (index.js) 127.42KB 32.56KB
plugin-map (index.js) 27.07KB 8.96KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 46.66KB 13.13KB
plugin-timeline (index.js) 39.21KB 11.85KB
plugin-tree (index.js) 15.07KB 5.33KB
plugin-view (index.js) 93.05KB 23.86KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 12.07KB 3.68KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 120.63KB 39.56KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.50KB 2.06KB
react (schema-input.js) 4.31KB 2.07KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.50KB 1.99KB
sdui-parser (codegen.js) 9.45KB 3.76KB
sdui-parser (dashboard-widget-options.js) 3.07KB 1.30KB
sdui-parser (index.js) 7.30KB 3.12KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 25.28KB 7.81KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.29KB 0.24KB
sdui-parser (validate.js) 23.87KB 7.83KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 4.12KB 1.61KB
types (authoring-nodes.js) 0.20KB 0.19KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (cloud.js) 0.20KB 0.18KB
types (complex.js) 4.44KB 2.07KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (dashboard-widget-layout.js) 2.06KB 0.96KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 1.13KB 0.65KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 5.78KB 2.70KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (node-slots.js) 7.18KB 2.34KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-approval-decision.js) 0.20KB 0.20KB
types (record-components.js) 0.20KB 0.19KB
types (record-panels.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 4.99KB 1.96KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 19.93KB 7.26KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.48KB 3.50KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 4cc23c7f77360ea0504e8fd9e3715eb810bfd2e8
Local-runs: none

Inputs read: card #12464 (body, triage 6190403616, claim 6190903933, dev report 6191722258), the split-off card #12486 (body), PR #12485 (body, file list, net diff against main at the merge base d9d9d9868), the 37 check-runs on this head (34 success, 3 skipped; Spec Main Shape Gate, Type Check, Changeset Declaration, Changeset Claim Re-read and Changeset Bump Policy all success), the spec's PageSchema.kind at objectstack c5dd7fce30 (packages/spec/src/ui/page.zod.ts, read from the objectstack checkout) and in the published @objectstack/spec@18.0.0-next.1 tarball (dist/ui/index.mjs, which objectui's lockfile resolves for every workspace package).

① Derived judgments

  1. The TS face narrows to 'full' | 'slotted' | 'html' | 'react' — right, and it extends the spec Page at both ends. At c5dd7fce30 the spec's kind is enumWithRetiredValues(['full', 'slotted', 'html', 'react'], PAGE_KIND_RETIRED_GUIDANCE).default('full'), and enumWithRetiredValues returns a plain z.enum(values, { error }), so the spec's own union is exactly these four. The published 18.0.0-next.1 (built 2026-10-14T12:08Z, before the retirement merged at 09:48Z the next day; gitHead absent) still declares kind: z.enum(["full", "slotted", "html", "react", "jsx"]).default("full") and COMPILED_PAGE_KINDS = ["html", "jsx"]. A four-member union is a subtype of both, so PageNodeSchema extends Omit<SpecPage, PAGE_SPEC_EXCLUDED | 'slots'> holds against next.1 (the Type Check run on this head) and against spec main (the Spec Main Shape Gate run on this head, which force-runs pnpm type-check --continue repo-wide against a spec built from objectstack main; the objectstack main head at the run's time, 8044f4d5 at 11:57Z, descends from c5dd7fce30, and page.zod.ts has not changed since). The triage's TS2430 red on main is therefore closed by this diff. The new pin page-kind-jsx-retired-12464.test.ts asserts Equal on the whole union and carries a @ts-expect-error on kind: 'jsx'; packages/types' type-check script runs tsc -p tsconfig.test.json, so the directive is in a compiled program, not a phantom check.

  2. The zod face (packages/types/src/zod/layout.zod.ts, outside the claim's surface): kind: stripImportedDefaults(SpecPageSchema).shape.kind.unwrap().extract([...four...]).optional() — right mechanism. On both spec versions kind is a ZodDefault over a ZodEnum; stripImportedDefaults turns the default into an optional, .unwrap() yields the enum, and .extract picks members by reference. On spec main the four are the whole enum (identity); on next.1 the pick drops jsx, so safeParse({ kind: 'jsx' }) fails at path kind (the vitest pin); and .extract is typed over the enum's own keys, so a spec that drops a kind fails this file's compile instead of silently diverging. It is the same shape as the existing PAGE_KIND_NODE_TYPE = PageTypeSchema.extract([...]) in the same file. A WiderThanDeclared ledger entry would have been the alternative, and the dev is right that it cannot be correct on both spec versions at once. Runtime readers, verified: the only non-test import of a page-carrying zod schema from @object-ui/types/zod is packages/cli/src/utils/authoring-face.ts (StrictAnyComponentSchema.safeParse, behind objectui validate / objectui check); the other three runtime importers (plugin-dashboard/measureRefusal.ts, plugin-designer/DashboardEditor.tsx, plugin-map/ObjectMap.tsx) parse dashboard-widget and map configs, not pages. The render path does not touch it: SchemaRenderer calls @object-ui/core's hand-written structural validateSchema (core/src/validation/schema-validator.ts imports no zod schema), and app-shell's MetadataService parses with the spec's own schemas, not objectui's zod face. So a stored jsx page served by a next.1 server is not refused anywhere on the render path; it still reaches the renderer's 'jsx' arm. No FAIL here. One observation, not a defect: on next.1 the objectui-side refusal carries zod's anonymous enum message (no html prescription), since next.1 ships no retired-value guidance; once the resolved spec carries c5dd7fce30, the spec's own prescription rides through .extract.

  3. page.tsx: const kind: string | undefined = schema.kind — right per triage's half-2 deferral. Narrowing the type made kind === 'jsx' a TS2367 in the components declaration build, so half 1 could not leave the file untouched. The widening annotation (no cast, no as) keeps every existing arm compiling and changes nothing at runtime: the 'react', 'html' || 'jsx' and template branches are byte-for-byte the same comparisons. No jsx to html mapping was added. The comment states the pin window honestly. The arm's removal stays with components: drop the page renderer's retired 'jsx' kind arm once the resolved @objectstack/spec carries objectstack c5dd7fce30 (half 2 of objectui#12464) #12486, as triage prescribed.

  4. Authoring and doc surfaces now present html only — right, with the leftovers accounted for. At this head, kind: 'jsx' is authored nowhere in apps/, packages/*/src (non-test), content/docs, skills or docs; react-pages.md and the layout.ts docblock now say the alias was retired and to author html; the console sample and its extraction pin read html. What still names jsx as current: the getJsxManifest comment in page.tsx, the re-attached-checks comment in layout.zod.ts (which describes next.1's COMPILED_PAGE_KINDS, so it is literally true today), and the app-shell server double in ResourceEditPage.pageRequiresStamp-11357.test.tsx (test-only, mirrors next.1). All three are named in the dev report and carried by components: drop the page renderer's retired 'jsx' kind arm once the resolved @objectstack/spec carries objectstack c5dd7fce30 (half 2 of objectui#12464) #12486 (its items 2 and 3). The sample's file name sdui-jsx-preview.* names the parse tier, carries no kind value, and is correctly left alone.

② Semver level

  • @object-ui/types: minor — matches the diff and the Clause-②: yes line. The published PageNodeSchema.kind narrows (a literal typed kind: 'jsx' stops compiling; the zod face refuses it), which is breaking, and objectui levels a breaking change minor (fixed group, major reserved for the cross-major sync with @objectstack; check-changeset-no-major passed). The changeset body spells out the breaking semantics and the html replacement, as the policy asks.
  • @object-ui/console: patch — right. The console is published (no private), its sample page's kind changed, and nothing about its contract moved.
  • @object-ui/components omitted — acceptable. The presence rule (AGENTS.md and check-changeset-presence.mjs) asks that a PR touching a fixed-group package's src/ carry a changeset declaration; it does not demand one entry per touched package, and Changeset Declaration passed on this head. In the fixed group @object-ui/components is versioned together with types regardless, so no bump is lost; the only effect is that its CHANGELOG carries no line for a type-annotation-only edit with unchanged behaviour, and the types changeset already records that the renderer is unchanged. Half 2's own @object-ui/components: minor is correctly reserved for components: drop the page renderer's retired 'jsx' kind arm once the resolved @objectstack/spec carries objectstack c5dd7fce30 (half 2 of objectui#12464) #12486, where behaviour does change.

③ Boundary flags

  • Out-of-surface edit, packages/types/src/zod/layout.zod.ts — answered. The claim's surface was derived from the card's premise that the zod directory carries no jsx kind; that was measured on literal text, and the zod PageNodeSchema inherited kind from the spec by reference. With the TS face narrowed, zod-mirror-parity.test.ts turned the package's own type-check red (two TS2322), so half 1 was not shippable without this file. The edit is the same narrowing's zod half, follows an in-file precedent, and the dev declared it in the report with the alternative (a ledger entry) and why it was rejected. I accept the in-place exemption; the PM should add the file to the claim surface on record. Escalation: none needed beyond that bookkeeping.
  • page.tsx edited in half 1 — answered. Forced by TS2367; the edit is a widening annotation and a comment, no behaviour, and the half-2 deliverable (dropping the arm) is untouched. Within the triage's intent.
  • Half 2 deferred to components: drop the page renderer's retired 'jsx' kind arm once the resolved @objectstack/spec carries objectstack c5dd7fce30 (half 2 of objectui#12464) #12486 — answered. The pin reading is correct (next.1 is older than the retirement commit; npm view @objectstack/spec@next is 18.0.0-next.1). components: drop the page renderer's retired 'jsx' kind arm once the resolved @objectstack/spec carries objectstack c5dd7fce30 (half 2 of objectui#12464) #12486 carries the Restart-when: line verbatim from triage, is pm:on-hold and marked not-a-claim, and its steps 1 to 5 cover restoring the kind read, the two stale comments, the app-shell test double, a non-compilation pin, and the components changeset. All three of the dev's noted leftovers are on it.
  • Model-free commit trailers — not a deviation. The three commits end with Claude-Session: plus Co-authored-by: Claude with no model identifier, which is the pair AGENTS.md prescribes; the harness's model-named reminder is the lower-precedence instruction. Nothing to escalate.
  • Verification claims vs. gates. The dev's NOT MEASURED items (Spec Main Shape Gate, check:spec-floors) are answered by CI on this head: the gate is success; spec-floors is the known main red and no PR gate names it.

Implemented-by: claude/issue-12464-retire-page-kind-jsx
Reviewed-by: session_01XVp19E1MYUTFM3dnkwz8gB

VERDICT: PASS


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

2 participants