Skip to content

fix(app-shell): PageView builds the page context, never reads a refused context key (objectui#9673) - #10541

Merged
objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-9673-pageview-drop-context-spread
Sep 25, 2026
Merged

objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-9673-pageview-drop-context-spread

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #9673
Clause-②: no

What changed

PageView no longer reads a page-level context key. The context it hands SchemaRenderer is now built only from the route and the refresh counter: exactly { params, refreshKey }. The (page as any).context read and the as any cast on it are gone. This is the ruling on the card (comment 5811058824, letter remove). context stays undeclared on PageSchema.

PageSchema refuses context. So every page that parses spread undefined there, and the line looked like an author channel that no author could use. context is written after ...page, so a stored document that never passed PageSchema and still carries context no longer gets it through to the node either.

Files:

  • packages/app-shell/src/views/PageView.tsx: the one context: line, plus a comment saying why it is built and not read.
  • packages/app-shell/src/views/__tests__/PageView.context-9673.test.tsx: new pin, run through the real PageView render.
  • .changeset/9673-pageview-drop-context-spread.md: @object-ui/app-shell patch.

Pin and reverse verification

The pin mounts the real PageView with SchemaRenderer stubbed to capture the node, using the same harness as page-kind-writing-end-9718. It has two cases:

  1. A page that parses (control: PageSchema.safeParse succeeds) and a query string account=42&tab=notes. The node's context must equal { params: { account: '42', tab: 'notes' }, refreshKey: 0 }.
  2. The same page with context: { leaked, params: forged } cast in (control: PageSchema.safeParse fails). The node's context must still equal { params: { account: '42' }, refreshKey: 0 }.

Reverse verification, run after the fix was committed: I put the spread back into the committed PageView.tsx with a trap-guarded replace. On disk, the injected text counted 1 and the fixed anchor counted 0. Case 2 went red (Tests 1 failed | 1 passed) and case 1 stayed green, as expected, because spreading undefined changes nothing on a page that parses. I restored the file with git checkout HEAD -- PATH. After that, git diff HEAD was empty, the file on disk matched the HEAD blob hash, and the pin was green again (Tests 2 passed).

Census (objectui#9438)

node scripts/page-key-read-census.mjs before the change: context [REFUSED], the PageView read, and 1 of 8 page key(s) ... REFUSED. After the change: OK every one of the 7 page key(s) read off the metadata cache is one PageSchema accepts, exit 0.

The census script and its test are unchanged. The dispatch expected a live-instance list to edit, but none exists. The script works out its findings on each run, and neither scripts/page-key-read-census.mjs nor its test names this site or pins a live count. Adding a count pin would write the instrument's answer into the tree, which AGENTS.md #9 forbids. The card's "two" live instances no longer hold either. The other one, the pageType read in usePageAssignment, was already retired by objectui#9674 (PR #10480). So after this PR the census reports zero refused reads, where the dispatch expected one.

Checks (at head 16f38e2)

  • Targeted vitest, pnpm exec vitest run on the new pin, page-kind-writing-end-9718, PageView.test.tsx and scripts/__tests__/page-key-read-census.test.ts: Test Files 4 passed (4), Tests 23 passed (23).
  • @object-ui/app-shell type-check (tsc --noEmit && tsc -p tsconfig.test.json), run after building the @object-ui/app-shell^... closure (28 packages): exit 0. --listFiles includes the new test.
  • eslint on the two touched .ts/.tsx files: 0 errors, 2 files by -f json. The only warnings are no-explicit-any (5 per file), and lint.yml does not set --max-warnings. PageView.tsx goes from 5 to 4 as any.
  • check:vi-mock-specifiers, check:vi-mock-inherit, check:vi-mock-override-shape, check:test-path-roots, check:new-line-citations (0 new), check:control-bytes, check:changeset-claims, check:pending-changeset-literals, check-changeset-presence, check-changeset-no-major: all exit 0.
  • Full packages/app-shell/ suite: NOT MEASURED. It ran past the roughly 10-minute foreground limit and was terminated. The local run is narrowed to every test file that imports PageView. CI's pnpm test covers the rest.

Acceptance notes

  • No non-test code under packages/*/src reads schema.context or context.params from a page node (hypothesis 2, by grep). None of the page fixtures in the tests that import PageView writes context. Nothing downstream depended on author-supplied keys, so no fixture needed a fix.

Generated by Claude Code

…ed `context` key

PageSchema refuses a page-level `context` key, so the spread of
`(page as any).context` contributed `undefined` on every page that parses.
The node's `context` is now exactly `{ params, refreshKey }` and the cast at
that seam is gone.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D486C1axmXnrkJMNUfz2eb
…, refreshKey }

Mounts the real PageView over a page that parses and over one that sneaks a
refused `context` key in; both must hand SchemaRenderer the route-built
context only (objectui#9673).

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

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3045.8 KB 3104.5 KB
Main entry chunk (gzip) 147.7 KB 350 KB
Entry file index-CN4Tcz1L.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) 16.68KB 6.20KB
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) 27.95KB 10.04KB
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.52KB 3.45KB
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.13KB 7.95KB
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) 544.97KB 130.34KB
core (index.js) 9.22KB 3.71KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 223.86KB 62.25KB
fields (index.js) 257.02KB 65.07KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
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.24KB 2.27KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.40KB 12.91KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 39.28KB 11.09KB
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.52KB 4.88KB
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) 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.01KB 3.93KB
plugin-calendar (index.js) 51.38KB 14.60KB
plugin-charts (index.js) 74.95KB 20.88KB
plugin-chatbot (index.js) 198.36KB 47.20KB
plugin-dashboard (index.js) 133.59KB 35.39KB
plugin-designer (index.js) 216.25KB 44.39KB
plugin-detail (index.js) 232.25KB 61.40KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 146.50KB 37.51KB
plugin-gantt (index.js) 169.37KB 41.83KB
plugin-grid (index.js) 215.49KB 58.91KB
plugin-kanban (index.js) 49.30KB 15.39KB
plugin-list (index.js) 114.55KB 28.29KB
plugin-map (index.js) 23.82KB 7.42KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.55KB 11.99KB
plugin-timeline (index.js) 30.64KB 8.94KB
plugin-tree (index.js) 10.52KB 3.69KB
plugin-view (index.js) 88.00KB 21.97KB
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) 114.58KB 37.60KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.03KB 1.86KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.78KB 2.09KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.78KB 2.56KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 18.27KB 6.20KB
types (ai.js) 4.11KB 2.06KB
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.93KB 1.49KB
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 (expression.js) 0.20KB 0.18KB
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) 5.00KB 2.39KB
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 (strict-authoring-face.js) 17.15KB 6.32KB
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 September 25, 2026 07:57
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Sep 25, 2026
Merged via the queue into main with commit c2c4372 Sep 25, 2026
45 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-9673-pageview-drop-context-spread branch September 25, 2026 08:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

2 participants