Skip to content

fix(plugin-view): the New button opens a create form under a split or popover navigation (objectui#10975) - #11014

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-10975-split-popover-create-surface
Sep 28, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-10975-split-popover-create-surface

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #10975
Clause-②: no

Under a split or popover navigation mode, ObjectView's New button now opens a create form. Implemented by the domain:ui seat 2 dispatch (PM claim 5874519919), session https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN.

What changed

  • packages/plugin-view/src/ObjectView.tsx: a renderCreateSurface helper draws the create form on the drawer or the modal when formMode is create. The split branch renders it, and so does the default branch when formLayout is popover.
    • formLayout, handleCreate, handleEdit, handleRowClick and both NavigationOverlay branches are unchanged. A record opened to view or edit still opens beside the list under split, and in the popover under popover.
    • The fix covers both sources of the mode: the node's own navigation, and the active named view's (read first since PR objectui#10967).
  • New pin file ObjectView.splitPopoverCreate-10975.test.tsx: 3 fix rows and 3 controls.
  • New changeset .changeset/10975-split-popover-create-surface.md: '@object-ui/plugin-view': patch.
  • Dated note appended to the pending .changeset/10885-object-view-route2-named-view-rest.md, in the objectui#10533 form. Its sentence "the New button opens no form" is now false.
    • The front matter is byte-identical: the sha1 of its first three lines is equal at base and head. The diff is 3 added lines and 0 removed.
  • packages/plugin-view/README.md and content/docs/plugins/plugin-view.mdx, the same two edits in each:
    • "Opening a record" gains one sentence: under split or popover the create form opens on the layout surface.
    • In "On the registered renderer", the sentence saying the create form's surface follows the named view gets the split / popover exception.

The chosen surface and why

The create form opens where the node's own non-split create path would open it. That is the surface formLayout falls back to with no navigation: layout, meaning schema.layout, or deriveRecordSurface when it is unset.

  • layout: 'modal': the modal (renderModalForm).
  • layout: 'drawer': the drawer (renderDrawerForm).
  • layout: 'page' with onNavigate: the form is never reached. handleCreate hands onNavigate('new', 'edit') to the router first, as before.
  • layout: 'page' without onNavigate: here the code has no single answer, because the non-split path has no surface for a page inside this component either. This case takes the drawer: it is what deriveRecordSurface answers for a light desktop object, and it is the registered renderer's defaultProps.layout.

A named view's navigation replaces the node's as a whole (objectui#10885 member 4). So under a named split, the node's own navigation does not choose the create surface; layout does. The README's Create section already says "the new-record form opens on the layout surface".

edit, decided explicitly: unchanged. handleEdit sets the record, so the split panel and the popover already draw the edit form beside it. The edit control below measures this, green on base and head.

Pins: red on base, green on head

ObjectForm is a sink that shows the mode its schema was built for. The drawer is found by vaul's data-vaul-drawer attribute, and the modal by role="dialog" with no drawer ancestor.

row base 2049b03df7 head
THE FIX: the node's own split, New gives a create form in the drawer red green
THE FIX: the node's own popover with layout: 'modal', New gives a create form in the modal red green
THE FIX: a named view's split over the node's modal navigation, New gives a create form in the drawer red green
CONTROL: a drawer navigation's New gives a create form in the drawer green green
CONTROL: under split, a row click opens the existing record beside the list (grid still mounted, Tasks Detail heading, not in a dialog) green green
CONTROL: under split, Edit opens the existing record beside the list green green
  • Base: a detached worktree at 2049b03df7 with the pin file copied in gave Tests 3 failed | 3 passed (6). The three failures are exactly the fix rows, each Unable to find an element by: [data-testid="object-form"].
  • Head: Tests 6 passed (6).
  • Ablation on head (ablation-replace.mjs, anchor must hit, WRAP mode): the renderCreateSurface body was replaced by null. Anchor x1 to x0, blob db64424727 to 16ecc8ecf0. Result Tests 3 failed | 3 passed (6), exactly the three fix rows. Restore proven: blob equals HEAD and git diff HEAD is empty.
  • Surface mutation: layout === 'modal' ? renderModalForm() : renderDrawerForm() was replaced by renderDrawerForm(). Result Tests 1 failed | 5 passed (6): only the modal row went red. Restored and proven the same way.
  • The pin imports ../ObjectView from source, so no dist sits between a mutation and the run.

Gates

These ran at head 0d122bf345 under the verify lock. VERDICT command-exit lines are quoted as the lock printed them.

  • pnpm --filter @object-ui/plugin-view type-check, after building the 15-task dependency closure: VERDICT command-exit 0. tsc -p tsconfig.test.json --listFilesOnly counts the new pin (1 hit) among 51 plugin-view test files.
  • vitest over packages/plugin-view/ plus the 11 out-of-package tests that read ObjectView.tsx off disk (found with git grep): Test Files 68 passed (68), Tests 1611 passed (1611).
  • vitest over 21 tests that read the edited docs or the pending-changeset corpus: Test Files 21 passed (21), Tests 810 passed (810).
  • eslint . in packages/plugin-view (the package's own lint script): 72 files, 0 errors. The new pin is among them.
  • Changeset gates:
    • check-changeset-presence: pass. check-changeset-no-major: pass. check-changeset-fixed: pass.
    • check-changeset-overwrite (report-only) names the modified 10885 file. Its declaration is @object-ui/plugin-view: patch both at base and now: the dated-note case.
    • check:changeset-claims (report-only) lists 6 pending changesets that name a touched file. Each paragraph was read, and none is made false. They cover the non-grid fetch, generateViewSchema, the retired sort key, the title relay and the objectui#7779 measurement. The self-contradiction reading passes.
    • check:pending-changeset-literals: pass.
  • Other gates:
    • check:new-line-citations: VERDICT new-cross-file-line-citations: 0 new citation(s). check:control-bytes: pass.
    • check-doc-links: "Links are valid across 17 scan roots". check:doc-types, check:doc-fences and check:doc-example-ids: pass.
    • check:vi-mock-specifiers, check:vi-mock-inherit, check:vi-mock-override-shape and check:test-path-roots: pass.
    • check:readme-exports: pass. Its first run exited 1 only because the cli and plugin-ai dists were missing, a prerequisite and not a finding; it passed once those were built.
    • check:handler-key-reads, check:icon-record-names, check:unreferenced-sources and check:i18n-keys: pass.
  • scripts/__tests__/markdown-test-inputs.test.ts is not owed: the new pin reads no markdown.
  • check-governed-queue-guard --test on the 6 paths: NOT GOVERNED.
  • git merge-tree --write-tree against a fresh main (ad0310fb57) is clean, tree fbba809560. main is one commit past the base (objectui#11010, app-shell only), with no file overlap.

Eager closure (console build, base against head)

turbo run build --filter=@object-ui/console was run twice in the same worktree. The two trees differ only in ObjectView.tsx, which was swapped to its base blob with a trap-guarded restore proven by hash.

base head delta
eager gzip bytes 3,179,018 3,179,053 +35
eager raw bytes 10,758,347 10,758,422 +75
eager chunks 330 330 0
  • The plugin-view chunk grew by 75 raw and 32 gzip bytes. index and CreateViewDialog grew by 1 and 2 gzip bytes, from renamed chunk hashes.
  • check:eager-closure is red on base, 18 bytes over the 3,179,000 ceiling (objectui#10996). Head is 53 bytes over. Bundle Analysis will be red for that pre-existing reason; this change adds 35 gzip bytes.

Acceptance notes

  • The docs sentence named in the dispatch was not where the dispatch said. The README and the docs page do not carry "the New button opens no form": 0 hits in both, against 1 hit in the pending changeset as the positive control.
    • What the fix does make false there is "the surface and width of the record, create and edit forms, follow the active named view".
    • Under a named split or popover, the create form's surface now comes from layout, though its width still follows the named navigation. That sentence is amended, and "Opening a record" now documents the node's own case.
  • layout: 'page' with no onNavigate, outside split / popover: formLayout is 'page', which has no render branch, so New opens nothing there either.
    • This is a read-only inference, not measured.
    • No in-tree JSON producer authors layout: 'page' without onNavigate.
    • Not filed.
  • The README's Create section says the new-record form opens on the layout surface. Under a drawer or modal navigation it opens on that navigation's surface instead. This wording drift predates the change and is not made false by it.
  • A closed drawer is now mounted at rest under split, because the initial formMode is create. That keeps the create drawer's exit animation, as on the drawer path. A closed drawer renders nothing into the DOM, and the row-click control is unchanged.

Generated by Claude Code

… / popover

Under a `split` or `popover` navigation mode, the node's own or the active
named view's, `ObjectView` draws a form only beside a selected record, and
`handleCreate` clears that record, so the New button set state that nothing
rendered. The create form now opens on the surface `formLayout` falls back to
with no `navigation`: the modal for `layout: 'modal'`, the drawer otherwise.
A record opened to view or edit still opens beside the list.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN
…opover

Adds the plugin-view patch changeset, a dated note on the pending objectui#10885
member-4 changeset whose "the New button opens no form" sentence this change
makes false (front matter untouched), and the README / docs-page prose: under
`split` or `popover` the create form opens on the `layout` surface.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN
@github-actions github-actions Bot added documentation Improvements or additions to documentation plugin tests labels Sep 28, 2026
@github-actions

Copy link
Copy Markdown
Contributor

changeset-claim-re-read

⚠️ 6 pending changeset(s) describe a file this change touches

Their bodies publish verbatim into the CHANGELOG at the next release, so this is a request to re-read them against your diff — addressed here because you are the one seat that can answer it without re-deriving anything.

⛔ Nothing here blocks, and nothing here is a verdict on your change. This gate exits 0, is not a required context, and judges name resolution, never meaning: it asked whether a pending body names a file you touched. "Is this sentence still true?" is the one question it will not answer, and the one you are being asked to answer.

.changeset/6726-find-envelope-records-arms.md

  • names plugin-view/src/ObjectView.tsx → packages/plugin-view/src/ObjectView.tsx — edited by this change

    | module | what it does | | --- | --- | | components/src/hooks/related-count-store.ts | related-list tab badge count | | components/src/renderers/basic/data-list.tsx | element:repeater rows | | components/src/renderers/basic/elements.tsx | element:number client-side aggregate | | components/src/renderers/basic/record-picker.tsx | element:record_picker options | | plugin-detail/src/renderers/record-activity.tsx | record:activity self-fetch | | plugin-detail/src/renderers/record-history.tsx | record:history self-fetch | | plugin-view/src/ObjectView.tsx | non-grid (kanban / calendar / gallery / timeline) fetch |

.changeset/7070-no-invented-gantt-date-fields.md

  • names plugin-view/src/ObjectView.tsx → packages/plugin-view/src/ObjectView.tsx — edited by this change

    • app-shell/src/views/ObjectView.tsx — the console object page. The inline branch becomes ganttViewOptions, the sibling of calendarViewOptions and timelineViewOptions: the declared block spread whole, title floored at 'name', no date field invented. - plugin-list/src/ListView.tsx — the render branch AND the capability gate. - plugin-view/src/ObjectView.tsx — generateViewSchema, the authored object-view element route, which bypasses ListView entirely.

.changeset/7499-gantt-non-axis-floors-omitted.md

  • names plugin-view/src/ObjectView.tsx → packages/plugin-view/src/ObjectView.tsx — edited by this change

    • plugin-list/src/ListView.tsx — the object-gantt render branch. - plugin-view/src/ObjectView.tsx — generateViewSchema, the authored object-view element route, which bypasses ListView entirely.

.changeset/8221-retire-legacy-string-sort.md

  • names content/docs/plugins/plugin-view.mdx → content/docs/plugins/plugin-view.mdx — edited by this change

    Docs teach the array only: content/docs/plugins/plugin-map.mdx, content/docs/plugins/plugin-view.mdx and packages/plugin-view/README.md.

  • names packages/plugin-view/README.md → packages/plugin-view/README.md — edited by this change

    Docs teach the array only: content/docs/plugins/plugin-map.mdx, content/docs/plugins/plugin-view.mdx and packages/plugin-view/README.md.

.changeset/8653-listview-title-retired-rowactiondefs-pinned.md

  • names packages/plugin-view/src/ObjectView.tsx → packages/plugin-view/src/ObjectView.tsx — edited by this change

    title — retired. ListView resolved its export filename through schema.label || (schema as any).title. @objectstack/spec/ui's ListViewSchema refuses title by name (unrecognized_keys: ['title']) while ObjectGridPropsSchema accepts it; packages/types mirrors the platform contract rather than ruling over it, so declaring title on ListViewSchema would have made this repo accept what the platform save gate rejects. That asymmetry is also why objectui#6639 could take the declare branch for ObjectGridSchema.title one package over and this site could not. A parse-based census of apps/ examples/ content/ and packages/ found zero list-view nodes authoring title, so the retirement costs no author a filename. Over that same corpus the instrument reports three object-grid nodes carrying the key: two authored ones, both in content/docs/api/schema-reference.md, plus one that is not authored at all — packages/plugin-view/src/ObjectView.tsx composes title: schema.table?.title onto a grid node it builds, so it is a producer writing the key rather than an author declaring it. ObjectGrid's own title reads are untouched — they remain declared, ruled and read.

.changeset/object-view-unmirrored-keys-7779.md

  • names packages/plugin-view/src/ObjectView.tsx → packages/plugin-view/src/ObjectView.tsx — edited by this change

    What was measured. Every reading was taken on the object-view node renderer (packages/plugin-view/src/ObjectView.tsx, registered by plugin-view/src/index.tsx) with schema.objectName / schema.layout as the positive controls of the same schema.KEY query, so each zero is a reading; the repo-wide census of viewTabBar finds the key in no source file outside @object-ui/types (two doc tables listed it as authorable and are corrected here). The spec side was read through the installed pin (@objectstack/spec@17.2.0, ui entry, 117 exported object schemas walked; control keys objectName / columns / navigation / listViews hit): the three spec-modelled keys are optional slots on ListViewSchema and ObjectListViewSchema; the six local keys have no spec slot anywhere.

Read the paragraph, not the line: both false halves of the objectui#8617 claim sat in one paragraph, and correcting either alone would have left it asserting the same wrong thing.

If a claim did go false, correct the body. That is precedented and prose-only, frontmatter untouched; check-changeset-overwrite.mjs will report the correction as its own case 2 ("correcting a declaration on purpose … legitimate"), which is the intended shape — one gate asks for the read, the other records the write.

Not covered, stated so nobody reads this as more: a born-false claim that spells no line address at all (objectui#9495 coordinated one by ORDINAL — "a grep finds that member first" — and deciding that means reading what the sentence means), a claim spelled as a symbol or a package rather than a backticked file name, and a file named ambiguously.

Compared the checked-out tree with ad0310fb5 (merge-base with origin/main): 4 file(s) changed outside .changeset/, read against 1706 pending declaration(s) that publish a body (2308 pending in total). · run

@github-actions

Copy link
Copy Markdown
Contributor

❌ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 330 chunks) 3104.5 KB 3104.5 KB
Main entry chunk (gzip) 149.8 KB 350 KB
Entry file index-BaDxDe0Y.js —
Status FAIL —

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.

Which half objected:

Eager-closure half Verdict
Aggregate closure ceiling ❌ over its ceiling
Per-chunk ceilings ✅ pass
Per-chunk membership (declared packages) ✅ pass
Ceiling sensitivity (headroom) ✅ pass
Ceiling freshness (checkout vs. base branch) ✅ pass

📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.58KB 6.17KB
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.22KB 10.61KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.17KB 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.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) 558.92KB 133.97KB
core (index.js) 9.93KB 3.94KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 227.62KB 63.16KB
fields (index.js) 261.01KB 66.28KB
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 (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.35KB 9.18KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 39.32KB 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.53KB 4.89KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.25KB 2.17KB
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) 52.39KB 14.98KB
plugin-charts (index.js) 84.09KB 22.93KB
plugin-chatbot (index.js) 198.22KB 46.97KB
plugin-dashboard (index.js) 137.56KB 36.67KB
plugin-designer (index.js) 215.78KB 44.42KB
plugin-detail (index.js) 233.49KB 61.79KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 162.67KB 41.95KB
plugin-gantt (index.js) 170.35KB 42.19KB
plugin-grid (index.js) 228.33KB 62.59KB
plugin-kanban (index.js) 48.43KB 15.11KB
plugin-list (index.js) 115.86KB 28.64KB
plugin-map (index.js) 22.90KB 7.62KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.17KB 12.20KB
plugin-timeline (index.js) 31.07KB 9.15KB
plugin-tree (index.js) 11.21KB 3.89KB
plugin-view (index.js) 89.54KB 22.44KB
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) 119.16KB 39.05KB
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) 7.50KB 3.05KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 6.16KB 2.71KB
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.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 18.27KB 6.22KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 3.83KB 1.49KB
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.26KB
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) 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) 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

Copy link
Copy Markdown
Contributor Author

Bundle Analysis is red here, and the red is not this PR's.

  • main itself is over the console eager-closure ceiling: 18 B over the 3,179,000-byte budget on the dev's base build. main's own Bundle Analysis push runs have been red since 6650259081. This is tracked on objectui#10996.
  • This PR's own delta is +35 gzip bytes (+75 raw): the renderCreateSurface helper and its two call sites in plugin-view's chunk. The dev measured it on a console build at base and at head, and the chunk count is 330 on both. Without this PR, the check would still be red.
  • No fix exists yet. Bringing main back under the budget is objectui#10996's to decide, and this PR's bytes are part of what it will measure.
  • Bundle Analysis is not in the repository's required set, so this PR lands on its required checks and review. No re-run: it would read the same main.

domain:ui seat 2 · 2026-09-28T17:3xZ

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 0d122bf345c88d43a7d383ce2bff804fad7ad7a3

This is an in-seat review. The diff is one render helper, two call sites, a pin file, a changeset, a dated note, and the README and docs pages (6 files, +190 −2), Clause-②: no, executing triage 5872733337. I read the whole diff against main ad0310fb57.

① Derived judgments

  1. The fix is gated on create alone.
    • renderCreateSurface returns null unless formMode === 'create'.
    • It draws renderModalForm() for layout === 'modal' and renderDrawerForm() otherwise, the same two renderers the drawer and modal branches use. Width and title come along unchanged.
    • It is mounted in the split branch and, behind formLayout === 'popover', in the default branch. No other branch renders it, so drawer, modal and page are untouched.
  2. No double surface.
    • handleCreate clears selectedRecord, and both beside-surfaces still require isFormOpen && selectedRecord, so a create form never draws twice.
    • handleRowClick (under split / popover), handleEdit and handleView set a record and a mode other than create, so an existing record still opens beside the list. After a create closes, formMode stays create behind a closed drawer or dialog until the next row click moves it, which renders nothing visible.
  3. page routes first. layout === 'page' with onNavigate still hands New to the router in handleCreate, before any state is set. Only page without onNavigate reaches the helper and takes the drawer. The code comment says so.
  4. Both sources of the mode. formLayout reads navigationConfig, which is the active named view's navigation when one is authored (objectui#10885 member 4). The named-view pin puts a named split over a node modal and gets the drawer, so the node's replaced navigation does not pick the surface.
  5. Pins (6 rows):
    • Three fix rows: the node's split, the node's popover with layout: 'modal' (asserted in a dialog and not in the drawer), and a named split.
    • Three controls: drawer New unchanged, a split row click, and a split Edit. Both beside controls assert the Tasks Detail panel, the grid still mounted, and no dialog.
    • The dev's base leg gives 3 failed / 3 passed, exactly the fix rows. The ablation of the helper's body turns exactly those three red. The surface mutation (always the drawer) turns only the layout: 'modal' row red, so that assertion is live.
  6. Docs.
    • The dated note is on the pending .changeset/10885-object-view-route2-named-view-rest.md. It is still pending on main at blob 1a1249e244, and its front matter is unchanged. It names the sentence it supersedes, "the New button opens no form", which appears in that changeset.
    • The README and docs pages add the same paragraph under Opening a record, plus one sentence in the named-view paragraph. Their "surface and width of the record, create and edit forms, follow the active named view" is now qualified.
    • The order was wrong on one point, and the dev measured it: it said the README and docs pages carry the "opens no form" sentence, but they carry 0 hits. The dev amended the sentence the fix does make false instead.
  7. One phrase is looser than its own gloss.
    • The new changeset and the dated note say the create form opens on "the surface the node uses with no navigation", then gloss it: the modal for layout: 'modal', the drawer otherwise.
    • For layout: 'page' without onNavigate, the node with no navigation opens nothing. That is the dev's first out-of-scope finding, so the phrase overstates for that one case. The gloss after the colon is exact and matches the code, so a reader who acts on it gets the shipped behaviour.
    • This does not block. The seat files that page case with its own carrier.

② Semver level

'@object-ui/plugin-view': patch, and Clause-②: no holds. renderCreateSurface is local to the component, and no declared type, accepted key or export moves.

③ Boundary flags

  • CI: this record posts only after the eight required checks at 0d122bf345 are final and green. The landing queue gates on them and re-runs git merge-tree against current main first.
  • Bundle Analysis: red. main is 18 B over the console eager-closure ceiling (objectui#10996), and this PR adds +35 gzip bytes, measured by the dev on a console build at base and at head. It is not a required check. One standing-down comment says so on the PR.
  • Merge-tree (dev): clean against main ad0310fb57. That includes PR objectui#11010, which touched app-shell only.
  • Governed surfaces: check-governed-queue-guard --test reports NOT GOVERNED on the 6 paths. The PR's own Governed Surface Queue Guard check-run decides.
  • State: draft, with assignee os-elon-musk. There is no needs:contract-review label, which fits Clause-②: no.

Implemented-by: claude/issue-10975-split-popover-create-surface
Reviewed-by: session_014mXUNuFomfj24w7s1pZzhN

VERDICT: PASS

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review September 28, 2026 17:37
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Sep 28, 2026
Merged via the queue into main with commit e9ca14c Sep 28, 2026
44 of 45 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-10975-split-popover-create-surface branch September 28, 2026 17:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation plugin tests

Projects

None yet

2 participants