Skip to content

feat(app-shell): approval request B1 build round: referenceVia record card, module-internal decision panel, routed request page (objectui#12045) - #12062

Merged
objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-12045-approval-detail-page
Oct 9, 2026
Merged

objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-12045-approval-detail-page

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Refs #12045 (B1 build round)
Clause-②: yes

The v18-independent build round of objectui#12045 (B1 of the approvals epic objectui#2763), under ruling 6079807016 (letter 乙) and claim amendments 6079939323, 6080124489 (amendment 2) and 6081385696 (amendment 3). This PR does not close the card. The page-mount step stays open on the card's three Blocked-by: lines.

What changed

  1. referenceVia pointer pairs draw A2's record card in the details grid (amendment 2: the details face only).
    • @object-ui/fields owns the rule. resolveRecordPointer(field, row) returns { objectName, recordId } (the new RecordPointer type) for a text field that declares a non-empty referenceVia, read from the row; otherwise it returns null. RECORD_POINTER_CARD_TYPE is the registry key a pair is drawn with. The package registers a default under it that draws the record id as text.
    • @object-ui/plugin-detail's DetailSection is the call site. It holds the row, reads referenceVia off the object field and draws the pair through the key.
    • @object-ui/app-shell registers RecordPreviewCard (A2) under the key with the existing registerFieldRenderer, from a side-effect module imported by src/index.ts. fields does not import app-shell.
    • Unchanged: CellRendererProps, packages/types, and the authorable keys.
  2. The decision panel, module-internal (amendment 3, route B).
    • packages/app-shell/src/views/approval-decision/ApprovalDecisionPanel.tsx draws A3's DecisionProgressIndicator from the request's served decision_progress, above DeclaredActionsBar. The bar shows sys_approval_request's own declared actions at record_section, and decisions go through ActionParamDialog.
    • It reads the record context only and takes no authorable props. After a decision it invalidates the request record and its timeline on the data bus.
    • ⭐ Moved to the page-mount step: the record:approval_decision registration, its side-effect import from src/index.ts, its known-schema-types.ts line and its @object-ui/types/zod arm. They land together there, derived from spec(ui) v18: declare the approval decision panel as a page component type with a strict ComponentPropsMap row (objectui#12045 B1, ruling 乙) objectstack#22472's v18 row.
    • Why: packages/cli/src/__tests__/registered-types-validate-ratchet-10859.test.ts measured 361 refused namespaced keys against its pin of 360 once the type was registered. The gate forbids raising the pin. The pin stays at 360.
  3. A1 wired to the request page, and the timeline routed.
    • The file that picks the data source, named before the edit: packages/app-shell/src/console/AppContent.tsx. DefaultAppContent hands useAdapter() to the generic :objectName/record/:recordId route, and ObjectView forwards the same adapter to its drawer and split overlays.
    • app-shell cannot import the console's A1, so the console now declares the more specific route sys_approval_request/record/:recordId in apps/console/src/AppContent.tsx. It is passed only as extraRoutes. The route mounts the same RecordDetailView over createApprovalRequestsDataSource({ host: adapter, scope: { kind: 'all' } }).
    • In apps/console/src/services/approvalRequestsDataSource.ts, a sys_approval_action read whose $filter is exactly { request_id: ID } reads GET /approvals/requests/ID/actions. The source applies the read's $orderby (the object-array form, by stored value), $top and $skip itself, and reports the whole timeline as total. It drops $select and $expand, and refuses any other parameter with UNSUPPORTED_QUERY_PARAM. Any other sys_approval_action read goes to the host unchanged.
  4. Narrow item, one condition: on a request's own page, RecordDetailView passes no object name to useRecordApprovals, so the read of approvals opened on the request itself is not sent.

Docs: packages/fields/README.md and content/docs/guide/fields.md describe the pointer pair. Changeset: .changeset/12045-approval-decision-panel.md, with @object-ui/fields minor and app-shell, plugin-detail and console patch.

Measurements

  • List cells: A2 has no compact form (RecordPreviewCard has only the card). Under amendment 2 the list face is not in this round.
  • First-load bytes, from apps/console/dist/eager-closure.json:
    • base e97a0e2d measured 3,239,763 B gzipped, and this head measured 3,243,943 B, so the closure grew by 4,180 B.
    • Removing only the pointer-card import from src/index.ts (a measured mutation, restored) gave 3,242,613 B, so the card costs 1,330 B.
    • The ceiling stays 3,281,467 B, which leaves 37,524 B of headroom. pnpm check:eager-closure printed "Console eager closure is 3167.9 KB gzipped across 290 of 2474 chunks (budget: 3204.6 KB, headroom: 36.6 KB)".
    • The field-renderer registry has no lazy path, and the panel is no longer imported, so nothing was made lazy.

Tests (all at head 358ef618a unless noted)

  • New pins:
    • packages/fields/src/__tests__/resolveRecordPointer-12045.test.tsx;
    • packages/plugin-detail/src/__tests__/DetailSection.recordPointer-12045.test.tsx, which pins sys_approval_request.record_id and the non-approval pair sys_activity.source_id;
    • packages/app-shell/src/views/record-preview/record-pointer-card-renderer.test.tsx, the real DetailSection with A2's card for both pairs;
    • packages/app-shell/src/views/approval-decision/ApprovalDecisionPanel.test.tsx;
    • apps/console/src/services/approvalRequestsDataSource.timeline.test.ts, the routed sys_approval_action pins;
    • apps/console/src/services/approvalRequestsDataSource.decisionPage.test.tsx.
  • The last file mounts the real RecordDetailView over A1 with an in-repo slotted-page fixture: actions holds record:approval_decision (registered by the test itself, per amendment 3), highlights is empty, a details slot is authored, and discussion is empty. It asserts:
    • the tally and Approve and Reject, gated by the served viewer;
    • Approve opens ActionParamDialog with a comment and a multi-file input, POSTs /api/v1/approvals/requests/ID/approve, and re-reads the request;
    • record_id draws A2's card for the target invoice;
    • the timeline comes from the actions route;
    • no /approvals/requests?object=sys_approval_request read is sent;
    • the console route wins over the generic record route, with a control for another object.
  • The census rows for the new registry key are in cellRenderers.objectLiteral-8596 (fields) and summaryChip.badgeFitCensus-8464 (plugin-detail; its fit-side count moved from 35 to 36).
  • Suites:
    • pnpm exec vitest run packages/fields/ packages/plugin-detail/ at f1499e95e: Test Files 491 passed, 2 skipped; Tests 6393 passed, 15 skipped; exit 0. Route B touched neither package after that.
    • At 358ef618a, the 45 RecordDetailView* suites, the panel, record-preview and approval-progress suites, DeclaredActionsBar.test.tsx, apps/console/src/services/, the two AppContent route suites, packages/cli/, the four script tests for known-schema-types and sideEffects, and the four new or edited fields and plugin-detail files: Test Files 90 passed (90), Tests 957 passed (957), exit 0.
    • The full app-shell suite was not run. It is CI's.
  • Type-check, at 358ef618a, exit 0 with 0 error TS each: pnpm --filter @object-ui/app-shell type-check, pnpm --filter @object-ui/console type-check, pnpm --filter @object-ui/fields type-check and pnpm --filter @object-ui/plugin-detail type-check.
  • pnpm exec eslint on the 16 touched TS files: 0 errors and 268 warnings. Two warnings are new, both react-refresh/only-export-components, the class of their neighbours: one for resolveRecordPointer in packages/fields/src/index.tsx, and one for approvalRequestRoutes beside systemRoutes in apps/console/src/AppContent.tsx.
  • Gates, all exit 0 at 358ef618a: check:control-bytes, check:test-path-roots, check:changeset-claims (report-only; see Acceptance notes), check:pending-changeset-literals, check:side-effects-array, check:vi-mock-specifiers, check:vi-mock-inherit, check:vi-mock-override-shape, check:new-line-citations, check:registry-bare-names, check:doc-types, check:prompt-keys, check:unreferenced-sources, check:i18n-keys, check:readme-exports, check:doc-snippets, check:doc-examples, check:doc-fences, check:handler-key-reads, check:self-import, check:phantom-deps, check:eager-closure, check:sdui-registration-pins, node scripts/regenerate-known-schema-types.mjs --check, node scripts/check-changeset-presence.mjs and node scripts/check-changeset-no-major.mjs. Locale packs are untouched, so the i18n pack gates do not apply.

Reverse checks. Each was run through scripts/ablation-replace.mjs, with the prediction written first. The tool proved the anchor landed on disk, and that the restore left the blob equal to HEAD and git diff HEAD empty.

  • A1: the sibling column hard-coded to object_name. Predicted and observed: 2 red (the non-approval pair in fields and in plugin-detail); the approval pair stayed green.
  • A2: the fields default not registered. 2 red (renderer of its own; the record id drawn as text).
  • A3: the DetailSection branch disabled. 2 red (both probe pairs); the control and the default stayed green.
  • A4: the app-shell card registration removed. 3 red (the registry answer and both cards); the control stayed green.
  • A5: the timeline invalidation dropped. 1 red.
  • A6: timeline routing disabled. 7 red (the routed and refusal cases); the 4 host controls stayed green.
  • A7: ordering not applied. 1 red.
  • A8: the narrow condition reverted. 1 red (the no-approvals-on-the-request assertion).
  • A9: the console route path changed. 1 red; the control stayed green.
  • A10: a registration re-added to the panel module. 1 red (the module-internal case). Its first attempt was refused by the tool, because the replacement contained the anchor, so nothing was mutated or measured. It was rerun with an anchor the replacement does not contain.

Acceptance notes

  • Moved to the page-mount step (amendment 3): the record:approval_decision registration, its barrel import, its known-types line and its zod arm.
  • Not in this round (amendment 2): the list-cell face for pointer pairs. A label read per row is one request per row, and batching target-row reads per object rides B2.
  • For plugin-approvals: serve sys_approval_request's detail page as a slotted page through the manifest pages, and declare the thread reply as approval_comment (objectui#12045 B1, ruling 乙) objectstack#22473's page: sys_approval_request declares record_id in its highlightFields. The details grid hides every field a mounted record:highlights shows, and the synthesized Details tab is also handed the object's highlightFields as hideFields. So the card draws on the served page only if that page keeps record_id out of the highlights strip and authors the Details body: this PR's fixture uses highlights: [] plus a details slot. Measured: with the synthesized strip, record_id sat in the highlights strip and no card was drawn.
  • Record surfaces covered: the console route covers the request's record route. That is the surface a list row opens: the object has at least RECORD_SURFACE_PAGE_THRESHOLD authorable fields and declares no list-view navigation, so its record opens as a page. A drawer or split overlay inside ObjectView (only if a view authors one), and the record views embedded by ObjectDataPage and InterfaceListPage, still read through the adapter. The carrier is the page-mount step.
  • Pending changeset text: .changeset/12032-approval-requests-data-source.md says "Nothing mounts the source yet". This PR mounts the source on the request's record route, so if both publish in one release the CHANGELOG states both. This PR leaves that changeset alone, because it is objectui#12032's declaration (check-changeset-overwrite). The seat decides whether to edit it.
  • $expand is dropped on the timeline read. The route serves actor_name in place of an expansion, and the related list resolves lookup labels through its own fallback.

Generated by Claude Code

claude added 3 commits October 9, 2026 12:10
… card, routed request page (objectui#12045)

B1 build round of the approvals epic: the parts that do not wait on v18.

- record:approval_decision: one panel that composes DecisionProgressIndicator
  (A3) with sys_approval_request's own declared actions through
  DeclaredActionsBar, over the record context, with no authorable props.
- @object-ui/fields owns the referenceVia pointer-pair rule
  (resolveRecordPointer, RecordPointer, RECORD_POINTER_CARD_TYPE) and a
  record-id-as-text default; the details grid resolves the pair from its row,
  and app-shell registers RecordPreviewCard (A2) under the key.
- The console mounts every sys_approval_request record page over the routed
  approvals source, and the source reads one request's sys_approval_action
  timeline from GET /approvals/requests/:id/actions.
- RecordDetailView skips the approvals-on-this-record read on a request's own
  page.

Claude-Session: https://claude.ai/code/session_01CGZy1BGCjdN5cXqL9cnvB8
Co-authored-by: Claude <noreply@anthropic.com>
…B1 build round (objectui#12045)

Claude-Session: https://claude.ai/code/session_01CGZy1BGCjdN5cXqL9cnvB8
Co-authored-by: Claude <noreply@anthropic.com>
… until its spec row (objectui#12045)

Claim amendment 3 (route B): no registration of record:approval_decision in
source, no barrel side-effect import, no known-schema-types line and no zod arm
this round. The registered-types ratchet pin stays at 360. The console's
slotted-page test registers the shipped renderer itself under the proposed
name; the registration, index import, known-types line and arm land together
in the page-mount step, derived from the v18 spec row.

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

github-actions Bot commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

changeset-claim-re-read

✅ Nothing to re-read

No pending changeset names a file this change touches, no address in this pull request's own prose points at a line this change moves, and no changeset here negates a package it declares. An earlier revision did. That request to re-read does not apply to the current diff.

This comment is updated in place on every re-run rather than posted again, so the thread does not grow one per push.

Compared the checked-out tree with 7282c6a51 (merge-base with origin/main): 19 file(s) changed outside .changeset/, read against 273 pending declaration(s) that publish a body (278 pending in total). · run

@github-actions

github-actions Bot commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 290 chunks) 3168.3 KB 3204.6 KB
Main entry chunk (gzip) 74.2 KB 350 KB
Entry file index-D-8qoYDk.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) 19.75KB 7.29KB
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) 41.19KB 11.12KB
auth (createAuthenticatedFetch.js) 8.54KB 3.46KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.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) 587.83KB 141.44KB
core (index.js) 10.00KB 3.96KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 240.39KB 67.09KB
fields (index.js) 269.42KB 68.20KB
i18n (LocalizationContext.js) 2.92KB 1.42KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.52KB 2.39KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.35KB 12.88KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 36.87KB 9.88KB
i18n (useSafeTranslation.js) 7.14KB 2.92KB
layout (index.js) 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.43KB 15.54KB
plugin-charts (index.js) 84.71KB 23.25KB
plugin-chatbot (index.js) 201.52KB 47.99KB
plugin-dashboard (index.js) 144.20KB 38.95KB
plugin-designer (index.js) 233.53KB 49.80KB
plugin-detail (index.js) 248.82KB 65.56KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 177.11KB 45.88KB
plugin-gantt (index.js) 179.17KB 45.07KB
plugin-grid (index.js) 249.62KB 69.16KB
plugin-kanban (index.js) 52.77KB 16.56KB
plugin-list (index.js) 120.51KB 30.42KB
plugin-map (index.js) 27.24KB 9.03KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.12KB 12.29KB
plugin-timeline (index.js) 39.06KB 11.80KB
plugin-tree (index.js) 15.07KB 5.33KB
plugin-view (index.js) 92.12KB 23.31KB
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.07KB 1.30KB
sdui-parser (index.js) 7.30KB 3.12KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 25.28KB 7.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.26KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.48KB 3.50KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

…erseded by objectui#12045 (seat-authorized rider)

Append-only: the frontmatter and every existing line are unchanged.

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

github-actions Bot commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 290 chunks) 3168.3 KB 3204.6 KB
Main entry chunk (gzip) 74.2 KB 350 KB
Entry file index-DEzw61VQ.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) 19.75KB 7.29KB
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) 41.19KB 11.12KB
auth (createAuthenticatedFetch.js) 8.54KB 3.46KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.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) 587.83KB 141.44KB
core (index.js) 10.00KB 3.96KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 240.39KB 67.09KB
fields (index.js) 269.42KB 68.20KB
i18n (LocalizationContext.js) 2.92KB 1.42KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.52KB 2.39KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.35KB 12.88KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 36.87KB 9.88KB
i18n (useSafeTranslation.js) 7.14KB 2.92KB
layout (index.js) 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.43KB 15.54KB
plugin-charts (index.js) 84.71KB 23.25KB
plugin-chatbot (index.js) 201.52KB 47.99KB
plugin-dashboard (index.js) 144.20KB 38.95KB
plugin-designer (index.js) 233.53KB 49.80KB
plugin-detail (index.js) 248.82KB 65.56KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 177.11KB 45.88KB
plugin-gantt (index.js) 179.17KB 45.07KB
plugin-grid (index.js) 249.62KB 69.16KB
plugin-kanban (index.js) 52.77KB 16.56KB
plugin-list (index.js) 120.51KB 30.42KB
plugin-map (index.js) 27.24KB 9.03KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.12KB 12.29KB
plugin-timeline (index.js) 39.06KB 11.80KB
plugin-tree (index.js) 15.07KB 5.33KB
plugin-view (index.js) 92.12KB 23.31KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 12.07KB 3.68KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 120.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.07KB 1.30KB
sdui-parser (index.js) 7.30KB 3.12KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 25.28KB 7.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.26KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.48KB 3.50KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 52d83afa3c44c11075696f73611f2b5e974a9d04
Local-runs: none

Inputs read: card objectui#12045 (body and all nine comments: claim 6078298531, measurement report 6078632273, decision request 6078697273, ruling 6079807016 letter 乙, amendments 6079939323, 6080124489, 6081385696, 6081923179, done report 6081836945); PR objectui#12062 (body, 21-file list, net diff against main, merge-base e97a0e2d); the 42 check-runs on the head. Nothing was built, run or re-run.

① Derived judgments

  1. @object-ui/fields public surface widens by three names and one registry key — resolveRecordPointer(field, row), the RecordPointer type, RECORD_POINTER_CARD_TYPE (record_pointer_card), with a default face registered under the key that hands value.recordId to TextCellRenderer. Right: exactly the surface amendment 2 (6080124489) declares; CellRendererProps and packages/types are untouched (neither is in the 21 files), and no authorable key is added.
  2. @object-ui/plugin-detail DetailSection gains one call site — a text field whose OBJECT definition declares referenceVia is drawn through the pointer face when both halves of the row are non-blank strings, otherwise it falls through to the type-aware renderer as before. Right: the one call site amendment 2 names; no plugin-grid file is touched, so list cells stay out, as ruled.
  3. @object-ui/app-shell — (a) a new load-time module record-pointer-card-renderer.tsx registers A2's RecordPreviewCard under the fields key through the existing registerFieldRenderer, is imported from src/index.ts, and package.json sideEffects names its dist and src paths; (b) RecordDetailView passes no object name to useRecordApprovals when the page object is sys_approval_request (one ternary); (c) ApprovalDecisionPanel.tsx is module-internal: no ComponentRegistry.register in source, no entry import, no known-schema-types.ts line, no @object-ui/types/zod arm, and ApprovalDecisionPanel.test.tsx pins ComponentRegistry.has('record:approval_decision') false. Right: (a) and (b) are amendment 1 items 1 and the narrow item, bounded as written; (c) is amendment 3 (6081385696) route B to the letter, and the ratchet registered-types-validate-ratchet-10859 stays at its pin (Test shards 1–8 success).
  4. Accept set: unchanged on this head. No component type is registered in source, so what page metadata objectui accepts does not widen here; the only ComponentRegistry.register('approval_decision', …, { namespace: 'record' }) lives inside approvalRequestsDataSource.decisionPage.test.tsx. referenceVia is a key the spec already declares and the parse already passes through; this head reads it at one call site, which changes what six objects' pointer fields SHOW on a details grid, not what metadata is accepted. Right, and consistent with amendment 3's reading that Clause-②: yes stands for the fields surface while the registered type's own clause belongs to the page-mount step.
  5. @object-ui/console — (a) apps/console/src/AppContent.tsx declares the object-specific route sys_approval_request/record/:recordId and passes it only inside extraRoutes (beside systemRoutes); extraRoutesNoApp keeps systemRoutes alone; the route mounts the same RecordDetailView over createApprovalRequestsDataSource({ host: adapter, scope: { kind: 'all' } }); (b) approvalRequestsDataSource.ts routes a sys_approval_action read whose $filter is exactly { request_id: ID } (object form, one key, non-blank string) to GET /approvals/requests/ID/actions, applies $orderby (object-array form only), $top, $skip locally with total = timeline length, drops $select and $expand, refuses any other present parameter with UNSUPPORTED_QUERY_PARAM before a request goes out, and passes every other sys_approval_action read to the host unchanged. Right: amendment 1 item 3 as amended by the dev's named file (packages/app-shell/src/console/AppContent.tsx picks the adapter; app-shell cannot import A1, so the console overrides the route), and pinned on the real view in decisionPage.test.tsx (timeline from the actions route, no host find of sys_approval_action, no /approvals/requests?object=sys_approval_request read, the console route winning with a control). The new exports APPROVAL_ACTION_OBJECT and approvalRequestRoutes are app-module exports, not a published package's.
  6. Rider on .changeset/12032-approval-requests-data-source.md — 2 lines appended, 0 removed; frontmatter and declared packages unchanged. Right: amendment 4 (6081923179) authorized exactly this shape; Changeset Overwrite Report is report-only and success.
  7. Not-on-it list held: none of the 21 files is under objectstack, packages/i18n/**, packages/components/src/ui/**, apps/console/src/components/StudioRoute.tsx, apps/console/src/pages/system/ApprovalsInboxPage.tsx, or a B2/B3 surface. The files beyond the claim (two registry census tests, packages/fields/README.md, content/docs/guide/fields.md, app-shell package.json) are each forced by a gate or by objectui AGENTS Add automated testing infrastructure and CI/CD workflows #2, as the done report's deviations state. Right.
  8. Gates are the verdicts: all 42 check-runs on the head conclude success, except Test (coverage), its shard and dependabot, which are skipped (conditional jobs); the Vercel status is success (ignored build step). Console Performance Budget: 3168.3 KB of 3204.6 KB, PASS. Type Check, Lint, Build & E2E, Test (dist pins), Changeset Declaration, Changeset Bump Policy, Changeset Fixed Group Check, Changeset Claim Re-read, Governed Surface Queue Guard, README Export Check, Doc Snippet Type Check, Inert vi.mock Specifier Check: success.

Nits (not verdict-bearing): (i) the export comment above approvalRequestRoutes in apps/console/src/AppContent.tsx names services/approvalRequestsDataSource.recordRoute.test.tsx as its consumer; the consumer in the diff is approvalRequestsDataSource.decisionPage.test.tsx. (ii) The changeset's console bullet carries the useRecordApprovals sentence, which is an app-shell change; app-shell's patch line covers it.

② Semver level

.changeset/12045-approval-decision-panel.md declares @object-ui/fields: minor, @object-ui/app-shell: patch, @object-ui/plugin-detail: patch, @object-ui/console: patch. Matches the diff: fields is the only published package whose entry gains names (three) and a registry key; app-shell adds a sideEffects module and a load-time face under a key fields owns, exports nothing new and registers no component type; plugin-detail exports nothing new and, with no host face loaded, draws the same text as before; the console is an app. The changeset's four bullets describe what the diff does, including that the panel is NOT registered yet. No major (Changeset Bump Policy success). The 12032 rider changes no declared package.

Clause-②: yes — on the PR body, and it matches the claim (6078298531) and amendments 1–3, which all carry Clause-②: yes for the fields surface; the registered type's own clause is the page-mount step's, as amendment 3 states. The review runs at CONTRACT_REVIEW_TIER for that line. Right.

③ Boundary flags

Dev flags from the PR's Acceptance notes and the done report's out_of_scope_findings / deviations:

  • F1 — registration, barrel import, known-types line and zod arm moved to the page-mount step. Answered: amendment 3 route B; the diff carries none of the four.
  • F2 — the list-cell face is not in this round. Answered: amendment 2; carried to B2 with per-object batching (A2's ACCEPT 6076152376).
  • F3 — sys_approval_request declares record_id in highlightFields; the details grid hides every highlighted field, so A2's card draws only on a served page whose highlights slot omits record_id and whose details slot is authored (the fixture's shape); with the synthesized strip no card was drawn. ESCALATED to the seat: an authoring constraint for plugin-approvals: serve sys_approval_request's detail page as a slotted page through the manifest pages, and declare the thread reply as approval_comment (objectui#12045 B1, ruling 乙) objectstack#22473 (the seat's card). Nothing on this head is wrong for it.
  • F4 — record surfaces covered. Only the app-scoped generic record route is overridden (extraRoutes); extraRoutesNoApp keeps systemRoutes alone; a drawer or split overlay inside ObjectView and the ObjectDataPage / InterfaceListPage embedded record views still read through the adapter (no viewer, no decision_progress). Amendment 1 item 3 says "every sys_approval_request record page"; this head covers the one a list row opens and names the page-mount step as the carrier. ESCALATED to the seat: whether a no-app record route exists in DefaultAppContent (not in this review's inputs) and whether the page-mount step's claim carries these surfaces explicitly.
  • F5 — the pending 12032 changeset said nothing mounts the source. Answered: amendment 4; the rider is on this head.
  • F6 — $expand is dropped on the timeline read; the route serves actor_name; the related list resolves lookup labels through its own fallback. Answered for this head. The done report's probe-only observation beside it: under mutation A8 alone, the timeline's request_id lookup-label read reached A1's list $filter refusal on sys_approval_request; it did not occur unmutated. ESCALATED to the seat as a watch item: that absence is not pinned; the seat decides whether the page-mount step pins it.
  • open_questions: [] on the done report. The measurement round's four (Q1–Q4) were ruled in 6079807016; none is open.
  • Deviations D1–D7 (two mid-round SendMessages answered by amendments 2 and 3; forced files; vi.importActual on a relative path into packages/app-shell/src with the SetupRoute.test.tsx precedent; origin/main not merged because packages/types is untouched, so the objectui#12049 serial condition did not trigger; eager closure +4,180 B within budget; model-free trailers; scratch probes and worktrees removed). Answered; none alters the verdict. The branch is 9 behind main; the merge group rebases.
  • Two new eslint warnings (react-refresh/only-export-components, the class of their neighbours). Answered: Lint success.

Implemented-by: claude/issue-12045-approval-detail-page
Reviewed-by: session_01CGZy1BGCjdN5cXqL9cnvB8

VERDICT: PASS

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 9, 2026 13:56
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 9, 2026 13:57
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 9, 2026
Merged via the queue into main with commit a368ccb Oct 9, 2026
45 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-12045-approval-detail-page branch October 9, 2026 14:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants