Skip to content

fix(types,layout,mobile,core): re-home the breakpoint vocabulary, delete the two dead responsive implementations - #7598

Merged
hotlong merged 2 commits into
mainfrom
claude/issue-7580-responsive-rehome
Sep 4, 2026
Merged

fix(types,layout,mobile,core): re-home the breakpoint vocabulary, delete the two dead responsive implementations#7598
hotlong merged 2 commits into
mainfrom
claude/issue-7580-responsive-rehome

Conversation

@claude

@claude claude Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Fixes #7580

Clause-②: yes

Implements the maintainer ruling of 2026-09-04 (batch #26, option A) recorded in comment 5535689433. All five numbered ruling items are adjudicated and were not re-litigated here. #4773 stays the measurement record and closes with this.

Fixes rather than Part of, deliberately: both halves land here, so nothing on the card is left executable. The one thing the card names that is NOT here is the @objectstack/spec pin bump, which the ruling explicitly places after this PR — see Sequencing below.

Why re-home rather than retire with the key

objectstack#11027 retired the whole ui/responsive vocabulary upstream on the stated ground that its four types "had no other authorable carrier". That is a claim about the whole surface, and it is measurably false on the renderer side:

  • responsive-grid is a registered SDUI component (packages/layout/src/index.ts), its columns input is authorable, and resolveColumnClasses applies it on the render path.
  • BreakpointName types four live readers in @object-ui/mobile (breakpoints.ts, useBreakpoint.ts, ResponsiveContainer.tsx) plus ResponsiveValue.

The tombstone's own return condition — the vocabulary "returns if and when a renderer implements it" — is therefore already met on this side, which is why the two types a renderer reads are re-homed and only the unread ones retire.

What changed

Re-homed, surface-identical:

  • BreakpointName is declared in @object-ui/types (mobile.ts) instead of re-exported from the spec. Same name, same six members, same export sites on @object-ui/types and @object-ui/mobileonly its provenance moved, so no consumer sees a difference.
  • BreakpointColumnMap is declared in @object-ui/layout (ResponsiveGrid.tsx), verbatim from the retired $strict schema: six optional column counts keyed xs2xl, no index signature restored.

Removed:

The objectstack#4115 comment is answered, not deleted (ruling item 5). The reason it recorded was correct and is now spent: with the spec's declaration retired there is nothing left for a local one to be mistaken for. A planted premise needs something to be wrong about.

The mechanism that makes the sequencing self-enforcing

The pin is still @objectstack/spec 17.2.0, which pre-dates the retirement and still exports both names. So for one pin interval a local declaration and a live spec export share a name, and check:spec-symbols fails on exactly that — measured, not assumed:

❌  a spec-named symbol is hand-written, not derived:
    • @object-ui/types declares 1 spec-named symbol the spec already owns:
        type `BreakpointName`  packages/types/src/mobile.ts:31  (exported by `@objectstack/spec/ui`)

This is held by two self-expiring entries in scripts/check-spec-symbol-derivation.mjs. They cannot outlive the interval: that guard's ratchet 3 fails an ALLOW entry that excuses nothing, so the moment the pin moves past the retirement the entries stop matching and the bump is forced to delete them.

This is not an invention — it is the repo's own precedent, followed step for step. objectui#5716 localized the three theme document types under the same option A, carried ALLOW entries "until the @objectstack/spec 17.2.0 refresh", and the entries "went stale exactly as their own comment predicted"; objectui#5668 deleted them and pinned the vacancy in page-nav-misc-spec-parity.test.ts. Same shape, same disposition, one vocabulary over.

For the same reason the three responsive rows in spec-ui-schema-reexports.test.ts are removed here ahead of that suite's ratchet firing — precisely how the ThemeModeSchema row was handled at objectui#5716.

Three sites outside the ruling's list

Each is in the card's own defect class (a consumption site of the retired vocabulary) and each would leave the bump red or a comment false. Naming them rather than folding them in silently:

  1. packages/types/src/index.ts:1349 re-exported ResponsiveConfig as SpecResponsiveConfig and BreakpointName as SpecBreakpointName straight from the spec — a second spec-import site the ruling does not name. Dead once its two readers went; dropped.
  2. packages/types/src/__tests__/spec-ui-schema-reexports.test.ts carried three responsive deny-list rows under a ratchet that fails on a row the spec has retired.
  3. packages/types/src/complex.ts:1037 asserted that the shared ResponsiveConfig shape "is NOT gone — it stays live on page.components[].responsive, which useResponsiveConfig really does read". This PR falsifies both halves. Corrected in place rather than left standing, because a stale liveness claim is what the next agent reads as the measurement. The same applies to one sentence in mobile.ts's MobileResponsiveConfig note, which cited the SpecResponsiveConfig re-export this PR drops; the rename still stands on its first ground, and the note now says so.

The suite the brief flagged — the PM's reading is falsified

packages/types/src/__tests__/spec-derived-unions.test.ts names BreakpointName and was expected to go red. Measured: it does not. @object-ui/types type-check is green with the re-home in place, and the suite is genuinely in that compilation set — tsc -p tsconfig.test.json --listFiles puts it among 560 files, so this is a measurement and not an unmeasured pass.

The reason: that case imports BreakpointName from ../mobile (never from the spec) and asserts a width'xs' | … | '2xl' satisfies BreakpointName — which the re-home preserves exactly. What expired is not the assertion but the comment above it, which grouped the symbol under the objectstack#4115 "ledger burn-down" population: declarations proved equivalent to the spec's and then replaced by a binding. This one went the other way.

So it is restated, not deleted. The case keeps its enforcement value — narrowing the now locally-owned union fails to compile rather than silently dropping a breakpoint — and the comment now records where the symbol goes next: at the pin bump it joins the suite's documented inverse population (names objectui uses that the spec does not have, each asserted absent). That absence cannot be asserted yet, because the pin still publishes the name. Deleting the case because its original reason expired would have removed live coverage on the exact declaration this ruling just made the repo responsible for.

The consumer census the ruling left open — still UNMEASURED, and here is the boundary

Whether hotcrm / cloud import BreakpointName from @object-ui/types was carried forward as an explicit confidence gap. It remains one, and the reason is now measured rather than assumed:

probe result
GET /repos/objectstack-ai/objectui HTTP 200
GET /repos/objectstack-ai/objectstack HTTP 200
GET /repos/objectstack-ai/cloud HTTP 403
GET /repos/objectstack-ai/hotcrm HTTP 403

403 rather than 404: the repositories exist, and this session is not authorized for them. Since GitHub code search only indexes what the caller can read, an org-wide search structurally cannot see them — its silence about those two repos is not evidence of absence, and is not reported as any.

What was measured, with positive controls:

  • Org-wide BreakpointName org:objectstack-ai: 14 hits — 9 in objectui, 5 in objectstack (all the retirement's own spec sources). The 9 match the in-repo census file-for-file, which is what makes the query trustworthy rather than empty.
  • In-repo control: BaseSchema lights 852 references against the same roots.
  • objectstack sibling checkout: zero consumption of @object-ui/types' BreakpointName, with @object-ui/ imports lit at 124 files as the control.

Why this does not gate the change: the re-home is a no-op for every consumer of BreakpointName — identical name, members and export sites. The unmeasured census bites on exactly one item, BreakpointOrderMap, whose removal from @object-ui/layout is a published-surface removal; that removal is ruling item 3, adjudicated, and in-repo readers are zero.

Verification

Measured on the pushed head, after the final origin/main merge.

run result
pnpm --filter {types,core,layout,mobile} run build green
pnpm --filter {types,core,layout,mobile} run type-check green — script names echoed, so not a zero-match pass
pnpm exec vitest run (four packages + the edited gate's own suite, repo root) 238 files, 4255 tests, all passed
pnpm check:spec-symbols ✅ 1343 files scanned against 4959 spec export names; 20 declared dialects (18 before: +2, exactly the two entries added; untriaged collisions unchanged at 14)
pnpm check:control-bytes ✅ OK (6223 tracked text files)
pnpm check:doc-types / check:dist-completeness green
eslint on the 9 changed lintable files 0 errors, 8 warnings — all no-explicit-any in complex.ts, identical at baseline HEAD; the change there is comment-only

Because I edited a gate script, that script's own suite was run separately from the derived families: scripts/__tests__/check-spec-symbol-derivation.test.ts plus check-doc-component-types.test.ts (2 files, 93 tests, green).

Declared narrowings, so they are not read as coverage:

  • check:readme-exports is NOT MEASURED locally, not red. All 317 findings are the single prerequisite class "type entry ./dist/index.d.ts is not on disk — run pnpm build first", from packages outside my build closure; zero name types/layout/mobile/core. The specific risk this diff could create — a README documenting a removed export — was measured directly instead: an exhaustive census of every name this PR removes from a published surface, across all 43 tracked READMEs, returns zero.
  • Repo-wide pnpm lint and the full 43-package downstream type-check were not run locally; CI runs the farm. In their place, the removal set was censused exhaustively by name (SpecResponsiveConfig, SpecBreakpointName, useResponsiveConfig, ResolvedResponsiveState, BreakpointOrderMap, BreakpointKey, BREAKPOINT_VALUES, ResolvedResponsiveConfig, resolveResponsiveConfig, getVisibilityClasses, getColumnClasses, getOrderClasses, shouldHideAtBreakpoint): zero live code references remain, every surviving mention being a comment written here explaining the removal, with the BaseSchema control lit at 852.

Sequencing — read this before the next pin bump

objectui's next @objectstack/spec pin bump must carry Blocked-by: #7580. The retirement is merged upstream and unreleased (spec pinned at 17.2.0; the tombstone cites RETIRED_DEFS_BY_MAJOR[18]), so this must land first. The pin bump is deliberately not done here.

Three things that bump owes, two of them mechanically forced:

  1. Delete both ALLOW entries in scripts/check-spec-symbol-derivation.mjsforced, ratchet 3.
  2. Add the absence pins for BreakpointName and BreakpointColumnMap to page-nav-misc-spec-parity.test.ts, and move the spec-derived-unions.test.ts case into that suite's inverse population — the disposition objectui#5668 used for the theme trio.
  3. ⚠️ Expect the name-ownership tripwire to fire, by design. page-nav-misc-spec-parity.test.ts asserts the spec still owns ResponsiveConfig, which is why MobileResponsiveConfig carries its renamed spelling. The retirement makes that false, so the row fires and says "re-run the triage". That is the mechanism working, and the triage it triggers is finding(types): retiring MobileComponentConfig leaves MobileResponsiveConfig and GestureConfig as zero-consumer published types #7519's, not this card's — the ruling keeps MobileResponsiveConfig there. Left untouched here deliberately: removing the row early would destroy the signal finding(types): retiring MobileComponentConfig leaves MobileResponsiveConfig and GestureConfig as zero-consumer published types #7519 needs.

Review posture

Clause-②: yes@object-ui/types gains an exported symbol declaration. needs:contract-review is on both carriers (this PR and #7580). Draft, and staying draft: per the tier amendment (5535740735) the implementation ran at opus under the quota-exhaustion exemption, which moves the implementation tier only — it does not move the contract-review floor and does not authorise landing. Landing is the domain:ui seat's, after an in-seat contract review at claude-fable-5-1.

🤖 Generated with Claude Code

https://claude.ai/code/session_01KbJQ1y1J12nZxYzFWhP8Q3


Generated by Claude Code

…ete the two dead responsive implementations (#7580)

objectstack#11027 retired `@objectstack/spec`'s whole `ui/responsive` vocabulary
on the ground that its four types "had no other authorable carrier". That ground
is measurably false on the renderer side: `responsive-grid` is a REGISTERED SDUI
component whose authorable `columns` is typed by `BreakpointColumnMap` and
applied by `resolveColumnClasses` on the render path, and `BreakpointName` types
four live readers in `@object-ui/mobile`. The tombstone's own return condition —
the vocabulary "returns if and when a renderer implements it" — is already met
here, so the two types a renderer reads are re-homed rather than retired.

Both halves land together: deleting only the dead implementations would leave the
next spec pin bump red, which is the card's central point.

Re-homed, under the same names and members:
- `BreakpointName` -> `@object-ui/types` (`mobile.ts`). No consumer change: same
  name, same six members, same export sites. Only provenance moved.
- `BreakpointColumnMap` -> `@object-ui/layout` (`ResponsiveGrid.tsx`), verbatim
  from the retired `$strict` schema (no index signature restored).

Removed:
- `BreakpointOrderMap` — ruling item 3; no read point, published only because the
  retired `ResponsiveConfigSchema` paired it with the column map.
- `useResponsiveConfig` (@object-ui/mobile) and `ResponsiveProtocol`
  (@object-ui/core), both measured at zero callers on #4773, with the parity test
  whose subject `ResponsiveConfigSchema` the retirement deletes.
- `SpecResponsiveConfig` / `SpecBreakpointName` — dead re-exports once the two
  implementations went; dropped rather than re-declared, the disposition the
  retired i18n names in that file already carry.

The objectstack#4115 comment is ANSWERED, not deleted (ruling item 5): the
retirement leaves no spec definition for a local declaration to be mistaken for.

The pin is still spec 17.2.0, which pre-dates the retirement and still exports
both names, so the collision is real for one pin interval and is held by two
SELF-EXPIRING entries in `scripts/check-spec-symbol-derivation.mjs` — ratchet 3
fails an ALLOW entry that excuses nothing, so the pin bump is forced to delete
them. Same mechanism objectui#5716/#5668 used for the theme trio.

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

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 50 chunks) 3182.6 KB 3191.4 KB
Main entry chunk (gzip) 143.2 KB 350 KB
Entry file index-CQ_IvV7x.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) 15.67KB 5.75KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 25.05KB 9.16KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.46KB 3.43KB
auth (index.js) 3.19KB 1.44KB
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) 5.13KB 2.35KB
collaboration (CommentThread.js) 26.08KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 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) 516.24KB 117.84KB
core (index.js) 6.12KB 2.42KB
create-plugin (index.js) 10.08KB 3.26KB
data-objectstack (index.js) 180.00KB 50.20KB
fields (index.js) 242.40KB 61.26KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 1.22KB 0.64KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 4.28KB 1.75KB
i18n (index.js) 3.65KB 1.47KB
i18n (pickLocalized.js) 7.62KB 3.26KB
i18n (provider.js) 26.89KB 9.04KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 38.98KB 10.98KB
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) 2.53KB 0.85KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 4.32KB 1.64KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 11.71KB 4.29KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 5.12KB 1.74KB
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) 15.75KB 3.80KB
plugin-calendar (index.js) 48.09KB 13.34KB
plugin-charts (index.js) 70.92KB 19.75KB
plugin-chatbot (index.js) 196.19KB 46.43KB
plugin-dashboard (index.js) 132.89KB 34.68KB
plugin-designer (index.js) 212.87KB 43.19KB
plugin-detail (index.js) 250.59KB 64.06KB
plugin-editor (index.js) 2.46KB 1.10KB
plugin-form (index.js) 132.87KB 32.66KB
plugin-gantt (index.js) 167.40KB 41.05KB
plugin-grid (index.js) 210.75KB 56.95KB
plugin-kanban (index.js) 52.71KB 14.55KB
plugin-list (index.js) 113.28KB 27.59KB
plugin-map (index.js) 20.55KB 6.80KB
plugin-markdown (index.js) 13.72KB 4.69KB
plugin-report (index.js) 43.57KB 11.96KB
plugin-timeline (index.js) 30.84KB 8.85KB
plugin-tree (index.js) 9.38KB 3.22KB
plugin-view (index.js) 85.24KB 20.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) 11.66KB 3.50KB
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) 81.07KB 26.86KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.63KB 2.18KB
react (schema-input.js) 2.32KB 1.24KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 5.41KB 2.34KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 4.93KB 2.24KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 20.57KB 5.88KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 10.35KB 3.60KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.74KB 1.41KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
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 (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
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) 4.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 4.58KB 2.23KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.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) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
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

hotlong commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Contract review: PASS at head 2b63279 — carriers cleared, landing through the queue

Director seat, summon 13, session_01WXyGTWPbbreqXow7Z2pZCk, 2026-09-04 ~05:40Z. Taken up because the domain:ui seat parked this PR on 5536055641 as complete work it cannot review (no fable in that lane); reviewing open needs:contract-review carriers is this seat's surface. Tier fuse: get_session at 05:33Z — session model and last served model both claude-fable-5-1 = CONTRACT_REVIEW_TIER.

Implemented-by: opus (os-dev subagent, session_01KbJQ1y1J12nZxYzFWhP8Q3, under the measured quota-exhaustion exemption recorded at 5535740735).
Reviewed-by: claude-fable-5-1 (this seat; diff, head tree on origin/claude/issue-7580-responsive-rehome, ruling 5535689433, dev report 5536036754 and the seat's delivery note all read).

① Derived judgments

  • @object-ui/typesBreakpointName moves from export type { BreakpointName } (a re-export of @objectstack/spec/ui) to a local export type BreakpointName = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' in mobile.ts. Same name, same six members, same unprefixed export site (index.ts:617); ResponsiveValue<T> unchanged. On the published .d.ts this is provenance only — no consumer can observe it. Removed from the surface: the prefixed re-exports SpecResponsiveConfig / SpecBreakpointName (type-only, both naming retired spec definitions; their two readers are deleted in this PR).
  • @object-ui/layoutBreakpointColumnMap becomes a local interface with the six optional column counts and no index signature, i.e. the retired $strict schema verbatim; responsive-grid stays registered (index.ts:206), export * from './ResponsiveGrid' still publishes the type, and resolveColumnClasses(columns) still runs on the render path (ResponsiveGrid.tsx:89/134). BreakpointOrderMap leaves the published surface — ruling item 3; the file's own prior comment recorded it had no read point.
  • @object-ui/mobile / @object-ui/coreuseResponsiveConfig (+ ResolvedResponsiveState) and the whole ResponsiveProtocol module (BreakpointKey, BREAKPOINT_VALUES, ResolvedResponsiveConfig, resolveResponsiveConfig, getVisibilityClasses, getColumnClasses, getOrderClasses, shouldHideAtBreakpoint) are deleted, with responsive-config-spec-parity.test.ts, whose subject no longer exists upstream. Zero callers on [finding] 响应式词汇的两个零消费表面:useResponsiveConfig 生产零调用者(page.components[].responsive 因此实际未生效)、MobileComponentConfig 零消费者 #4773; at this head a grep over packages/*/src, apps and examples for every removed name returns only the explanatory comments. The four live BreakpointName readers in packages/mobile are untouched and were never spec-importing.
  • The guard entries — two ALLOW rows in scripts/check-spec-symbol-derivation.mjs, both stating the interval (pin 17.2.0 pre-dates objectstack#11027) and not a divergence; ratchet 3 forces their deletion at the pin bump. The The theme TYPE surface (Theme, ThemeMode, ColorPalette, …) still derives from @objectstack/spec/ui, which retired it — objectui cannot refresh its spec pin past 17.1.0 until this is re-homed or dropped, and the choice needs a ruling #5716 / Build Docs is red on main: fs reaches the browser bundle via @objectstack/specpg-connection-string #5668 theme precedent, followed exactly. check:spec-symbols moved 18 → 20 declared dialects with untriaged collisions unchanged at 14, so nothing was pushed into debt.
  • Testsspec-derived-unions.test.ts keeps the width pin on the now locally-owned union (the seat's expectation that it would go red was falsified by the dev and correctly so: the case never imported the spec); spec-ui-schema-reexports.test.ts drops the three responsive deny-list rows ahead of its own ratchet, the ThemeModeSchema disposition. Both readings hold on the head tree.
  • Ruling item 5 — the objectstack#4115 comment in mobile.ts is rewritten to say the reason is spent (no spec definition remains to be mistaken for), citing objectstack#11027. Answered, not deleted.

② Semver vs changeset

minor on @object-ui/types, @object-ui/layout, @object-ui/mobile, @object-ui/core, with the explicit "Breaking, deliberately" paragraph. This repo refuses major (scripts/check-changeset-no-major.mjs), so minor plus the breaking statement is the correct spelling — the same one PR #7577 (#5928) used one day earlier for a rename with no alias. Changeset Bump Policy, Changeset Declaration and Changeset Fixed Group Check are green on the head. Consistent.

③ Boundary flags — the consumer census the lane could not run is now measured

The dev and the seat both reported GET /repos/objectstack-ai/cloud and /hotcrm as HTTP 403 for that lane and refused to read the org search's silence as absence. This seat reaches both repos. Read on origin/main (cloud 4c799d50, hotcrm 5e08628), with positive controls:

name cloud hotcrm
BreakpointName / BreakpointColumnMap / BreakpointOrderMap 0 / 0 / 0 files 0 / 0 / 0 files
useResponsiveConfig, ResponsiveProtocol, SpecResponsiveConfig, SpecBreakpointName, ResolvedResponsiveState, BreakpointKey, BREAKPOINT_VALUES, resolveResponsiveConfig, getVisibilityClasses, getColumnClasses, getOrderClasses, shouldHideAtBreakpoint 0 files 0 files
control: files importing @object-ui/ 16 3
control: files importing @object-ui/core, mobile, layout or types 0 2

So the one published-surface removal the census bore on (BreakpointOrderMap) has zero external readers in the two repos that were unreadable from the lane, and the dev's open question resolves as its own recommendation A: accept as-is. The access question the seat carried up (a lane that must decide published-surface removals cannot read two consumer repos) is real and is reported to the maintainer separately; it did not need to gate this PR.

Sequencing stands as the PR body records it: objectui's next @objectstack/spec pin bump deletes both ALLOW entries (forced), adds the absence pins, and expects the page-nav-misc-spec-parity tripwire on ResponsiveConfig to fire for #7519's triage. Landing this PR first is what makes that bump safe.

CI on 2b63279: 32 check runs, all completed, 0 failures (Lint, Type Check, Test shards 1–4, Build & E2E, all changeset gates green); mergeable_state clean.

Landing (this stroke): needs:contract-review cleared on this PR and on #7580 (read-modify-write, read back), PR marked ready, auto-merge squash armed — objectui lands only through the queue. The seat's pm:dispatched on #7580 clears with the auto-close.


Generated by Claude Code

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

Projects

None yet

3 participants