Skip to content

fix(app-shell): Details and the catalog read a package the runtime refused to load as not loaded (objectui#11760) - #11766

Merged
objectstack-fleet[bot] merged 5 commits into
mainfrom
claude/issue-11760-not-loaded-other-faces
Oct 7, 2026
Merged

objectstack-fleet[bot] merged 5 commits into
mainfrom
claude/issue-11760-not-loaded-other-faces

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #11760
Clause-②: no

The install-local listing marks a package this runtime refused to load at startup with notLoaded: { code, requiredRange } (objectstack PR #21833, @objectstack/* 17.7.0). Installed Apps reads that marker since objectui#11645 (PR objectui#11759). This PR makes the other faces read it too. Before, all of them told an operator that a refused package was installed and running.

Implemented by the dispatched dev on claude/issue-11760-not-loaded-other-faces, session session_01CGZy1BGCjdN5cXqL9cnvB8.

What changes

Face Before, for a notLoaded entry Now
Details header (MarketplacePackagePage, localInstalledBadge), catalog view and offline local view green "Installed · vX" outline "vX" + destructive "Not loaded", and the reason line beneath the badges
Details' own Uninstall (doUninstallLocal) confirm marketplace.uninstall.confirm, result successInDetail ("remains loaded … until the next restart") confirmNotLoaded / successNotLoaded
Catalog card (MarketplacePage, installedByManifestId) green "Installed vX" outline "vX" + destructive "Not loaded"
"Your organization" card on the same page (installedIds) green "Installed" destructive "Not loaded" (still no Install button: it is installed)
  • A loaded entry renders exactly as before on every face (measured below, byte for byte).
  • No new key, export, prop or type member. Every text is one objectui#11645 added: marketplace.notLoaded.badge, .protocolIncompatible, .otherReason, marketplace.uninstall.confirmNotLoaded, .successNotLoaded. marketplace.versionBadge already existed.
  • Files: the two pages, two new pin files beside them, one changeset. Nothing outside the claim's file surface.

Decisions the dispatch left to the dev

  • Shape. I followed Installed Apps' row: the version as an outline badge, then the destructive "Not loaded" badge, then (on Details) the reason line. The green badge carried the installed version, so keeping it beside "Not loaded" loses nothing. On the catalog view of Details and on a catalog card the catalog's latest version badge stays where it was, so a second "vX" can appear. It is the installed one, next to "Not loaded".
  • The reason line is on Details only. A catalog card is a compact click-through, and Details (one click away) carries the reason. The code-to-sentence mapping is the one Installed Apps uses (OS_PROTOCOL_INCOMPATIBLE names the range; any other code is named by otherReason).
  • successInDetail vs successNotLoaded. Read both in en.ts: the only Details-specific wording in successInDetail is "cached manifest for", inside the restart sentence that the not-loaded case drops. Nothing Details-specific is lost, so no new key.
  • The org card is the fourth "Installed" on the catalog page. The card names the catalog card (installedByManifestId). The org section of the same page badges a local install "Installed" through installedIds, which reads the same listing. Org packages have no public listing, so for them the org card IS the catalog's face. It is the same defect class, in a claimed file, with the same key, fixed mechanically in the shape this PR pins. Its own pin and control are below. If the reviewer reads it as beyond the card, dropping it is one ternary and two test cases.

Tests

New pins, both driven by the listing body the server serves (unmocked marketplaceApi over a stubbed fetch, the refused entry in objectstack PR #21833's wire shape: marker present, withSampleData absent), through the real I18nProvider in en and zh, with expected text read from the packs:

  • MarketplacePackagePage.notLoadedHeader-11760.test.tsx (17 cases): header and Uninstall, catalog view and offline local view, en and zh, each with a loaded-entry control (green "Installed · vX", no reason line, confirm / successInDetail), plus an unknown refusal code.
  • MarketplacePage.notLoadedBadge-11760.test.tsx (8 cases): catalog card and org card, en and zh, each with a loaded control.

Runs, from the worktree root:

  • The two new files: 25 passed (17 + 8).
  • pnpm exec vitest run --maxWorkers=2 packages/app-shell/ at 91c70b3: Test Files 1056 passed | 1 skipped (1057), Tests 10342 passed | 9 skipped (10351), lock VERDICT command-exit 0 (held 53m56s on a shared box).
  • 91c70b3 is the head before I merged main (d50f724) as the dispatch asks. That merge brings in only main's own landed commits. None of them touches these files, and the diff of b67f24f against main is exactly this PR's five files. The lock-bound reruns at b67f24f (dependency rebuild, type-check, the marketplace tests) are NOT MEASURED locally: the lock was held by another dev's full app-shell suite for more than 30 minutes. CI runs them on this head.

Type-check at 91c70b3: pnpm --filter @object-ui/app-shell type-check exit 0 (script name echoed as type-check, after building the dependency closure with pnpm --workspace-concurrency=2 --filter '@object-ui/app-shell^...' build, exit 0). tsc -p tsconfig.test.json --listFiles lists both new test files (control: marketplaceDates.displayLocale-10331.test.tsx listed).

Reverse verification, one face at a time

From committed da68b10, each leg went through ablation-replace.mjs (anchor must hit once, on-disk blob change proven, self-restoring with a trap, restore proven by blob equal to HEAD and an empty git diff HEAD). Each leg replaced one face's notLoaded condition with false:

Leg Pins red Everything else
Details header badge 5 (header x 2 views x 2 langs, unknown code) 12 green
Details reason line the same 5 12 green
Details Uninstall confirm 4 (Uninstall x 2 views x 2 langs) 13 green
Details Uninstall result the same 4 13 green
Catalog card 2 (en, zh) 6 green
Org card 2 (en, zh) 6 green

All loaded-entry controls stayed green in every leg. After the run, both source blobs equal HEAD (91ed0c30, 0e29f945) and git diff HEAD is empty. No dist leg: the tests import the pages by relative path, and @object-ui/i18n is aliased to src by vitest.config.mts.

Loaded entries are byte-identical

A one-off probe (deleted, never committed) rendered every face over loaded entries only, at head and with the base 7b17705 blobs of both pages checked out, and compared innerHTML: Details catalog view (en 5038 bytes, zh 5040), Details offline view (en 4359, zh 4333), catalog page with a catalog card and an org card (en 12255, zh 12221). All six were cmp-identical to base.

Gates

Re-run at b67f24f (lock-free), each exit 0, verdict line as printed:

  • pnpm check:control-bytes: OK (scanned 7772 tracked text files)
  • pnpm check:test-path-roots: OK
  • pnpm check:changeset-claims: No pending changeset names a file this change touches.
  • pnpm check:pending-changeset-literals: No test source names a pending changeset.
  • pnpm check:new-line-citations: 0 new citation(s)
  • pnpm check:vi-mock-specifiers, check:vi-mock-inherit, check:vi-mock-override-shape: OK
  • pnpm check:phantom-deps, check:self-import: OK
  • pnpm check:i18n-keys: every in-scope call-site key resolves against the en pack
  • pnpm changeset:check: no major; node scripts/check-changeset-presence.mjs: 4 source files of 1 released package changed, 1 changeset declared
  • node scripts/check-governed-queue-guard.mjs --test (5 paths): NOT GOVERNED
  • pnpm exec eslint --no-inline-config --format json on the 4 touched .tsx files: 4 results, 0 errors, 13 warnings, none on a line this diff adds. All 4 files are in eslint's population (isPathIgnored false), and the resolved config is not type-aware (no parserOptions.project / projectService), so the diff cannot move the verdict on an untouched file. Repo-wide pnpm lint is left to CI.

At 91c70b3: pnpm --filter @object-ui/app-shell type-check exit 0, and the full app-shell suite above.

Not run locally: the locale-pack gates (check:i18n-drift, -dead-keys, -designer-parity), because no pack changed; check:readme-exports, because no export changed; check:sdui-registration-pins and check:eager-locale-catalogues, which need a console build, because no registration and no import changed. scripts/__tests__/vite-declared-lazy-views.test.ts names these two paths but reads only AppContent.tsx and the package manifest, neither touched. It is NOT MEASURED (two lock queue timeouts, exit 99), and CI runs it.

Acceptance notes

  • The code-to-sentence reading of the marker now lives in two places: InstalledListWidget (notLoadedReason) and MarketplacePackagePage (same name). A later refusal code needs both edited. InstalledListWidget.tsx is outside this card, so the two were not merged into one helper. Observation only, not filed. Carrier: none.
  • Read from source and through the tests above. Not reproduced through a running console against a real refused package.

Generated by Claude Code

claude added 5 commits October 7, 2026 05:26
…fused to load as not loaded (objectui#11760)

The install-local listing marks an entry this runtime refused to load at
startup with `notLoaded: { code, requiredRange }`. Installed Apps reads it
since objectui#11645; three other faces still drew such an entry as
installed:

- the Details header badge ("Installed · vX") now draws the installed
  version and the destructive "Not loaded" badge, with the reason line
  beneath, in both the catalog view and the offline local view;
- Details' own Uninstall confirms and reports with the not-loaded texts
  (`confirmNotLoaded` / `successNotLoaded`) instead of the "stays loaded
  until restart" ones;
- the catalog card's "Installed vX" badge, and the org section's
  "Installed" badge on the same page, read "Not loaded" for such an entry.

A loaded entry renders as before on every face. No new key.

Claude-Session: https://claude.ai/code/session_01CGZy1BGCjdN5cXqL9cnvB8
Co-authored-by: Claude <noreply@anthropic.com>
…install as not loaded (objectui#11760)

Driven by the install-local listing body the server serves (unmocked
marketplaceApi over a stubbed fetch), through the real I18nProvider in en
and zh, with a loaded-entry control on every face: the Details header in
both the catalog view and the offline local view, Details' own Uninstall
confirm and result, the catalog card and the org card.

Claude-Session: https://claude.ai/code/session_01CGZy1BGCjdN5cXqL9cnvB8
Co-authored-by: Claude <noreply@anthropic.com>
… header badge (objectui#11760)

So a header regression and an Uninstall regression each turn their own
cases red, and nothing else.

Claude-Session: https://claude.ai/code/session_01CGZy1BGCjdN5cXqL9cnvB8
Co-authored-by: Claude <noreply@anthropic.com>
…s and the catalog (objectui#11760)

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

github-actions Bot commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 334 chunks) 3514.5 KB 3551.8 KB
Main entry chunk (gzip) 156.3 KB 350 KB
Entry file index-CbuH_upf.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) 17.82KB 6.58KB
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.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.70KB 10.94KB
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.11KB 7.97KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.28KB 2.60KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
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) 578.94KB 139.21KB
core (index.js) 10.00KB 3.96KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 235.41KB 65.46KB
fields (index.js) 266.88KB 67.46KB
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) 38.37KB 10.31KB
i18n (useSafeTranslation.js) 7.14KB 2.92KB
layout (index.js) 41.50KB 11.82KB
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) 13.86KB 5.00KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.52KB 2.26KB
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) 53.39KB 15.52KB
plugin-charts (index.js) 84.26KB 23.05KB
plugin-chatbot (index.js) 199.63KB 47.46KB
plugin-dashboard (index.js) 144.22KB 38.99KB
plugin-designer (index.js) 231.46KB 48.87KB
plugin-detail (index.js) 247.73KB 65.20KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 176.62KB 45.75KB
plugin-gantt (index.js) 179.17KB 45.07KB
plugin-grid (index.js) 238.51KB 65.53KB
plugin-kanban (index.js) 52.17KB 16.37KB
plugin-list (index.js) 116.85KB 29.12KB
plugin-map (index.js) 25.60KB 8.62KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.12KB 12.29KB
plugin-timeline (index.js) 39.10KB 11.81KB
plugin-tree (index.js) 15.07KB 5.33KB
plugin-view (index.js) 90.64KB 22.85KB
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.81KB 3.58KB
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.08KB 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.80KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
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-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) 4.99KB 1.96KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 19.93KB 7.25KB
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

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 7, 2026 07:29
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 7, 2026 07:29
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 7, 2026
Merged via the queue into main with commit 1bc17da Oct 7, 2026
45 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-11760-not-loaded-other-faces branch October 7, 2026 07:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

2 participants