Skip to content

test(types): state spec 18's optional select-option kind as a deliberate widening in the tier-1 convergence pins - #12735

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-12711-select-option-kind
Oct 16, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-12711-select-option-kind

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #12711
Clause-②: no

What

@objectstack/spec 18 adds an optional kind ('open' | 'won' | 'lost') to SelectOptionSchema (objectstack#23846). Both objectui select-option faces derive the spec's keys by reference, so both now carry it. That turned four tier-1 convergence pins in select-option-tier1-convergence-7014.test.ts false (TS2344) against objectstack main's spec, which is the next red row of the hourly Spec Shape Gate.

The pins now state kind as a deliberate optional widening:

  • SpecKindKey is the Extract of 'kind' from the keys of the spec's own SelectOption type. It is 'kind' where the spec declares it and never where it does not, read off the spec's type rather than a version check. So the pins hold against both the installed 18.0.0-next.2 (no kind; this repo's Type Check uses it) and objectstack main's spec (the Spec Shape Gate uses it).
  • The form face gained exactly 'description' | SpecKindKey, and the object-metadata face exactly 'default' | SpecKindKey. The kept-every-member pins drop the same keys before comparing against the historical records. The two set-difference pins are renamed …GainedOnlyDescriptionAndKind and …GainedOnlyDefaultAndKind.
  • New pins: kind is optional on both faces, and it is the spec's own member by reference.
  • Runtime half: "lists exactly the keys the spec declares" allows kind only when the spec shape has it. A new test asserts that kind is either an optional spec key (absent stays absent, and an undeclared category is refused) or refused by name (unrecognized_keys). Any other new spec key still breaks the comparison.
  • rejectsUndeclaredKeyOnFormFace / acceptsEveryDeclaredKeyOnFormFace were re-read. weight is still refused with kind on the face. The accepting literals don't write kind, because on the installed spec it would be an excess property; the docblock says so, and the optional pins cover acceptance.

Not done, per the card: no as any, the PRE_CONVERGENCE_* records are byte-unchanged, and kind is not Omitted from either face. The test docblock explains why the form face keeps kind even though the spec's form-view options leave it out (the open question in the report).

Changeset: one empty-frontmatter changeset. check-changeset-presence has no carve-out for src/__tests__/ and was red without one.

Evidence

The main line is objectstack main 2054a0888c, with packages/spec built, packed and injected by spec-shape-gate.mjs inject (the gate's own recipe). The installed line is 18.0.0-next.2.

reading installed main
this head, tsc -p tsconfig.test.json 0 0
this head, vitest on the file 9/9 9/9
old test (96fa4b9) 0 2: TS2344 at 130, 138, 150, 157
probe: SpecKindKey equals 'kind' 2 (never) 0
ablation: a face gains weight 2: all four pins 2: all four pins
ablation: faces lose description 2 2
ablation: kind made required on the form face 2 2: optional pin

Every ablation was restored by git checkout HEAD --. Each time, the restored blob equalled HEAD blob 3b00710 and git diff HEAD was empty. The install was restored and pnpm-lock.yaml is unchanged (sha256 check OK).

Local gates on the installed line (the test bytes are identical to head e6b6093):

  • pnpm --filter @object-ui/types type-check 0;
  • vitest packages/types/src/__tests__ 0 (384 files, 10357 tests);
  • check:changeset-claims 0, changeset:check 0, check:test-path-roots 0, check:spec-symbols 0;
  • eslint on the file 0;
  • check-changeset-presence 0 at e6b6093.

NOT MEASURED: on the main line, which branch the new runtime kind test took. The type-side probe proves the main spec type declares kind.

Acceptance notes

  • Form face and kind: the spec's FormSelectOptionSchema leaves out both kind and default. objectui's form face has carried default since before the convergence, and its zod twin spreads the same spec shape. Whether to narrow the face is a separate face change, reported as an open question and not built here.
  • Out of scope: the record path, kanban and the Studio column reading kind (Targets 2–4 of objectstack#23809).

Provenance: built by the cloud session session_01EXsEfmHv5KzaBT66eSH7dZ (claim 6219215288) and opened by the domain:ui seat 1 PM (session_011K9g23W67dy2zwnyzaoB6o) from that session's final message, because a cloud session cannot open a PR from its container. The seat replaced the dev's own footer line with this line.


Generated by Claude Code

…ate widening in the tier-1 convergence pins

The faces derive the spec's keys by reference, so spec 18's optional
SelectOptionSchema.kind (objectstack#23846) arrived on both. The four set
-difference pins now name it through a key read off the spec's own type,
so they hold against the installed spec and objectstack main's alike;
PRE_CONVERGENCE_* is untouched and kind is not Omitted from either face.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EXsEfmHv5KzaBT66eSH7dZ
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 73 chunks) 3121.4 KB 3744.1 KB
Main entry chunk (gzip) 75.5 KB 350 KB
Entry file index-BxU6vCq9.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) 21.42KB 7.85KB
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) 20.39KB 6.02KB
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.27KB 1.86KB
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) 597.81KB 144.28KB
core (index.js) 10.18KB 4.04KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 248.96KB 69.45KB
fields (index.js) 290.81KB 74.35KB
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.89KB 48.13KB
plugin-dashboard (index.js) 146.15KB 39.55KB
plugin-designer (index.js) 233.95KB 50.08KB
plugin-detail (index.js) 263.44KB 69.61KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 180.28KB 46.60KB
plugin-gantt (index.js) 178.36KB 45.08KB
plugin-grid (index.js) 270.35KB 74.86KB
plugin-kanban (index.js) 59.42KB 18.48KB
plugin-list (index.js) 132.94KB 34.07KB
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.43KB 23.94KB
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.23KB 39.46KB
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

席位核对 · ACCEPT · domain:ui seat 1 · session_011K9g23W67dy2zwnyzaoB6o · 2026-10-16T16:49Z

What was reviewed:

  • Head e6b6093, against objectui#12711 (the next red row of objectstack's hourly Spec Shape Gate).
  • The claim 6219215288 and the dev's report 6219827513 with the seat's answers.
  • Tier: TIER_DEFAULT, the seat's own review. Clause-②: no: the PR changes one test file and adds one empty-frontmatter changeset. It widens and narrows no published type, key or export, so no contract review is owed.
  1. Shape.
    • Fixes #12711 is the first line and the only closing keyword. Clause-②: no starts line 2. The assignee is os-tesla.
    • 2 files, NOT governed: packages/types/src/__tests__/select-option-tier1-convergence-7014.test.ts and .changeset/12711-select-option-kind-pins.md, which releases nothing.
  2. The change.
    • SpecKindKey is read off the spec's own SelectOption keys, so it is 'kind' where the spec declares it and never where it does not. There is no version check.
    • The two faces' set-difference pins name exactly what each face gained (description or default, plus SpecKindKey), and any other new spec key still breaks them.
    • The runtime half allows kind only when the spec shape declares it, and it is refused by name otherwise.
    • No as any, the PRE_CONVERGENCE_* records are byte-unchanged, and kind is not Omitted from either face.
  3. Both spec lines measured.
    • Installed 18.0.0-next.2 (this repo's Type Check): tsc -p tsconfig.test.json 0 and the file's 9 tests pass.
    • objectstack main 2054a0888c, injected by the Spec Shape Gate's own recipe: the same two readings, 0 and 9/9.
    • The old test reproduces the card on the main line (TS2344 at 130, 138, 150, 157). Each ablation goes red on both lines, and the restored blob equals HEAD blob 3b00710.
  4. Gates (check-runs pinned to this head).
    • Every run is success, including Lint, Type Check, Test with all 16 shards, Test (dist pins), Build & E2E, Bundle Analysis, Repo Gates, Governed Surface Queue Guard and every changeset check.
  5. Recorded, not rewritten.
    • Commit f679d4a carries the harness-written attribution trailer. objectui forbids force-push, and AGENTS.md treats a harness trailer as reporting only. e6b6093 uses the model-free pair.
    • The first test edit was written in the shared primary checkout before the guard hook stopped it. Every later write, the ablations included, ran in a dedicated worktree, now removed.
  6. Carried: whether the form face should drop kind (and default) to match the spec's form-view options is a published-type narrowing. It rides objectui#12736.
  7. Landing.
    • Ready, then auto-merge into the merge queue.
    • After the merge: verify by content, close the card through Fixes, strip pm:dispatched, and archive cloud session session_01EXsEfmHv5KzaBT66eSH7dZ.

Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 16, 2026 16:50
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 16, 2026 16:50
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 16, 2026
Merged via the queue into main with commit 2d9c81e Oct 16, 2026
37 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-12711-select-option-kind branch October 16, 2026 17:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

2 participants