Skip to content

fix(app-shell): an interface page relays its source view's hiddenFields and fieldOrder (objectui#10638) - #10667

Merged
objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-10638-interface-list-view-keys
Sep 25, 2026
Merged

objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-10638-interface-list-view-keys

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #10638
Clause-②: no — an interface page begins honouring two declared per-view keys it drops today; no export, schema or accept set moves.

Executes triage 5833520187 (bug · p2) under claim 5833614033 (domain:ui seat 2, session session_014mXUNuFomfj24w7s1pZzhN). This PR puts the source view's hiddenFields and fieldOrder into the list schema InterfaceListPage hands ListView. It states the page config's precedence and mirrors the objectui#7516 relay pin.

What changed

  • The relay (packages/app-shell/src/views/InterfaceListPage.tsx). The schema literal now gets two conditional spreads beside columns: hiddenFields: view.hiddenFields and fieldOrder: view.fieldOrder. Each is present only when the view authored the key and the view supplies the columns. The page composes nothing. ListView's effectiveFields memo already runs columns projects → hiddenFields subtracts → fieldOrder sorts the survivors, and an unlisted survivor sorts last.
  • The pin (InterfaceListPage.viewFieldKeysRelay-10638.test.tsx). ListView is stubbed and its schema prop is captured, the same posture as ObjectView.fieldOrderRelay-7516.test.tsx. Three FIX cases: the view's hiddenFields arrives verbatim; the view's fieldOrder arrives verbatim; the whole composition arrives together. One PRECEDENCE case and two CONTROLs: an empty view columns, and a view that authors neither key.
  • The file header (rounds 2 and 3). It now states sourceView as a fallback: the view's columns (with its hiddenFields / fieldOrder) and sort are inherited unless the page defines its own columns / sort, and its base filter is always inherited with the page's filterBy appended (ADR-0047 revised). The "never restated" wording it replaces was false on base as well.
  • Changeset .changeset/10638-interface-page-view-field-keys.md, '@object-ui/app-shell': patch.

The precedence (H2): the whole composition, from one source

cfg has no spelling of either key. InterfacePageConfigSchema is a strictObject, and its alias map sends fields / columnList to columns and neither key anywhere. So the two keys resolve with the page's column list, as one unit, on the same three branches columns already used:

  1. The page authors its own columns. That list is used as it stands, and neither view key applies. Released spec (@objectstack/spec 17.4.0): page columns are "Defined directly on the page (no view inheritance)", and sourceView is "Still honored at runtime as a fallback when the page has no own columns".
  2. Otherwise the view's non-empty columns. The view's hiddenFields and fieldOrder come with them.
  3. Otherwise object-derived defaults. Neither key applies. ListViewSchema.columns says an empty list "declares no projection, so neither of them applies". This is spec source at objectstack origin/main (objectstack#19598, 9dcdb77), not yet in the released 17.4.0.

Why not "the page's columns projects, the view's keys subtract and sort", as the order's H2 sketched? The composition is declared per view. The ListViewSchema composition docblock says "Three keys on this schema together build one field list"; this is spec source at objectstack origin/main (objectstack#19598), not yet in the released 17.4.0. Mixing sources would also break design mode. The design-mode column drag (onColumnStateChange) saves the order it shows as the page's columns. A relayed view fieldOrder would then sort that list straight back, so the drag would appear to do nothing. Ablation B below shows the pin catches that wiring.

Premises, measured on base 4a3d500fd before the first edit

  • H1 holds. The site is as the card describes: resolveSourceView feeds a schema literal with 0 mentions of either key. Fixture: columns: ['name','owner','stage'], hiddenFields: ['owner'], fieldOrder: ['stage','name'].
    • What ListView receives on base: the pin reads Tests 3 failed | 3 passed (6), expected undefined to deeply equal [ 'owner' ] and … [ 'stage', 'name' ]. schema.columns arrives as ['name','owner','stage'].
    • What it draws, from a one-off probe that was not committed: the real InterfaceListPage and the real ListView, with only object-grid stubbed to record the column list it is handed. Base draws ["name","owner","stage"]. Head draws ["stage","name"].
  • H2. The precedence is set out above. The PRECEDENCE case is green on base by construction, because base relays neither key. What it catches is an over-broad relay; see ablation B.
  • H3. The relay only delivers the values; ListView is untouched.
  • H4. The pin goes red on base and green on head, with per-hunk ablations below.
  • H5. No sentence needed reconciling:
    • No interface-page doc and no packages/app-shell/README.md sentence names which source-view keys the page honours.
    • Of the pending changesets naming InterfaceListPage, only .changeset/7218-rowcolor-host-relay.md names the edited file (per check:changeset-claims). Its paragraph ("has shipped rowColor: view.rowColor next to grouping and pagination") was re-read and is still true.

Verification of the change, round 1, on b172bf0fb

All runs went through the shared verify lock. Legs 2–4 ran in one hold, each with a trap-protected restore proven by the blob hash matching HEAD (00977917bcae) and git diff HEAD empty.

  • Head: pin Tests 6 passed (6). Probe draws ["stage","name"].
  • Base file swapped in (git show 4a3d500fd:…; on disk the viewComposes count is 0 and the blob equals the base blob f3e2e80e0509): pin Tests 3 failed | 3 passed (6), the three FIX cases. Probe draws ["name","owner","stage"].
  • Ablation A: the relay hunk deleted, both spread lines. Tool: ablation-replace.mjs (objectstack), anchor x1 → x0, blob 00977917bcae → 740fff063f76; an in-run grep -c read 0 and 0. Predicted: three FIX cases red. Measured: Tests 3 failed | 3 passed (6), the three FIX cases. The probe draws ["name","owner","stage"].
  • Ablation B: the precedence gate removed (viewComposes && view. → true && view., --expect 2, anchor x2 → x0, replacement x0 → x2), so the keys are carried unconditionally. Predicted: PRECEDENCE and the empty-columns CONTROL red. Measured: Tests 2 failed | 4 passed (6), exactly those two.
  • Dependency closure: turbo run build --filter='@object-ui/app-shell^...' --concurrency=2. Tasks: 28 successful, 28 total.
  • @object-ui/app-shell type-check: tsc --noEmit && tsc -p tsconfig.test.json, exit 0. --listFilesOnly on the test project lists the new pin.
  • Tests that name InterfaceListPage (23 files across 9 packages): Test Files 23 passed (23), Tests 362 passed (362).
  • Lint, narrowed and proven:
    • Population from ESLint's own config: isPathIgnored is false for both changed .ts/.tsx files.
    • Count: 2 files, 0 errors. InterfaceListPage.tsx has 39 warnings on head and 39 on base. The pin has 9 no-explicit-any warnings, the same as its sibling InterfaceListPage.mapConfig.test.tsx.
    • Invariance: no type-aware linting (parserOptions.project / projectService unset), and no rule under eslint-rules/ reads the filesystem, so this diff cannot move a verdict on any untouched file.
  • Gates (lock-free), with each gate's own verdict line:
    • check-changeset-presence ✅ 2 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)
    • check:new-line-citations VERDICT new-cross-file-line-citations: 0 new citation(s)
    • check:control-bytes ✅ OK
    • check-changeset-no-major ✅
    • check-changeset-fixed ✅
    • check:pending-changeset-literals ✅
    • check:vi-mock-specifiers / -inherit / -override-shape ✅ OK
    • check:test-path-roots ✅ OK
    • check:changeset-claims: report-only; the one paragraph it names was re-read, as above
    • check-governed-queue-guard --test on the three paths: NOT GOVERNED

Round 2, on 2a26e30ec (wording only, no code change)

The contract review passed the change and asked for three wording fixes:

  1. The file header clause now states sourceView as the ADR-0047 (revised) fallback. The replaced text said "never restated", which was false on base too.
  2. The changeset now reads "carried that view's columns, filter, sort (and its other keys)", so the list is no longer read as exhaustive. The frontmatter is byte-identical: md5 of its first three lines is 4e8ce55021fe3b14329c8b4cb3f9f354 before and after.
  3. This body marks which spec quotes are released (17.4.0) and which are spec source at objectstack origin/main (objectstack#19598).

Gates on 2a26e30ec:

  • check-changeset-presence ✅ 2 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)
  • check-changeset-no-major ✅
  • check:control-bytes ✅ OK
  • check:new-line-citations VERDICT new-cross-file-line-citations: 0 new citation(s)
  • @object-ui/app-shell type-check: exit 0, after the closure build (Tasks: 28 successful, 28 total, all cached)

Merge check: fresh origin/main ed8251189 has not touched the three files since base, and git merge-tree is clean, so no merge commit was needed.

Round 3, on 106a48803 (one comment clause)

The round-2 re-review found the header clause false on its filter leg: the page always spreads the view's filter and appends its own filterBy (:437-440 at this head), so the page never displaces the base filter. The clause now says so. Comment only; check:control-bytes and check:new-line-citations pass, the app-shell type-check exits 0, and git merge-tree against origin/main f905090a1 is clean.

Not run locally; these are CI's: the full pnpm test farm, Lint, E2E, and the repo-wide pnpm lint.

Acceptance notes

  • The page still takes the source view's filter, sort, grouping, rowColor, pagination, userFilters and the other keys whether or not it authors its own columns. This PR does not change that; it scopes only the two new keys. Read from source, not measured.
  • The object route (ObjectView) handles a columns: [] view differently. It fills in default columns, then relays that view's hiddenFields / fieldOrder, so the keys subtract from and sort the defaults there. This page applies neither key to its defaults. Carded by the seat as objectui#10694.

Generated by Claude Code

…ds and fieldOrder

InterfaceListPage hand-projects the source view's columns, filter and sort
into the list schema it hands ListView, and carried neither hiddenFields nor
fieldOrder. Both now ride with the view's columns, as one unit: they apply
only when the view supplies the columns (the page's own columns, or an empty
view projection, carry neither). ListView's effectiveFields composes them.

Pin: InterfaceListPage.viewFieldKeysRelay-10638.test.tsx.

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

github-actions Bot commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

changeset-claim-re-read

⚠️ 1 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/7218-rowcolor-host-relay.md

  • names InterfaceListPage.tsx → packages/app-shell/src/views/InterfaceListPage.tsx — edited by this change

    This is a relay, not a new surface. The interface route (InterfaceListPage.tsx) has shipped rowColor: view.rowColor next to grouping and pagination since ADR-0047, into a schema typed ListViewSchema, with no fence of any kind — so the key was already author-reachable and already had a delivery path; two of three hosts simply did not use it. The legacy shorthand for the same feature (bare color) already had a rung in both literals; only the spec-canonical spelling was missing.

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 f905090a1 (merge-base with origin/main): 2 file(s) changed outside .changeset/, read against 1531 pending declaration(s) that publish a body (2117 pending in total). · run

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3051.6 KB 3104.5 KB
Main entry chunk (gzip) 147.9 KB 350 KB
Entry file index-sk7jHECQ.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.57KB 6.15KB
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.17KB 10.58KB
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.39KB 1.21KB
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) 546.99KB 130.83KB
core (index.js) 9.22KB 3.71KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 223.91KB 62.28KB
fields (index.js) 260.14KB 65.89KB
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.49KB 14.64KB
plugin-charts (index.js) 74.94KB 20.89KB
plugin-chatbot (index.js) 198.36KB 47.20KB
plugin-dashboard (index.js) 133.60KB 35.41KB
plugin-designer (index.js) 216.25KB 44.39KB
plugin-detail (index.js) 232.85KB 61.58KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 152.20KB 39.05KB
plugin-gantt (index.js) 169.71KB 41.93KB
plugin-grid (index.js) 215.37KB 58.92KB
plugin-kanban (index.js) 48.35KB 15.08KB
plugin-list (index.js) 114.58KB 28.31KB
plugin-map (index.js) 22.42KB 7.38KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.55KB 11.99KB
plugin-timeline (index.js) 30.67KB 8.95KB
plugin-tree (index.js) 10.52KB 3.69KB
plugin-view (index.js) 87.77KB 21.99KB
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) 116.21KB 38.14KB
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) 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

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: b172bf0fb8421d2fcb8feb1290ef9221532e9ca3

① Derived judgments

Precedence ruling — holds. Spec read at objectstack origin/main 16c5a33f, packages/spec/src/ui/page.zod.ts. InterfacePageConfigSchema is strictObject (:543); its alias map (:546-558) sends only fields / columnList to columns; the shape (:567-615) has no hiddenFields or fieldOrder key. Spec's own words: comment :571-573 "ADR-0047 (revised): the page carries its OWN view metadata — columns, sort and base filter are defined directly here … The page IS the view definition."; columns describe :575 "Columns shown by the page. Blank = all object fields. Defined directly on the page (no view inheritance)."; sourceView JSDoc :581-583 "@deprecated Back-compat only. Pre-revision pages inherited columns/filter/sort from a named object view; new pages define columns/sort/filterBy directly. Still honored at runtime as a fallback when the page has no own columns." Both page-side sentences are also in the installed @objectstack/spec 17.4.0 dist. The spec never names the two keys on the page side, so at key level it is open; the governing sentence is the sourceView JSDoc, which makes the whole named view a fallback gated on "no own columns". That supports branch 1 and rules out the seat's sketch (applying the view's keys over page columns honours the view precisely when the page has own columns).

Design-mode drag — reason holds, read at source. Head InterfaceListPage.tsx:592: onColumnStateChange={onConfigChange ? (st) => { if (st?.order?.length) onConfigChange({ columns: st.order }); } : undefined} — the drag persists cfg.columns. ListView hands the grid columns: effectiveFields (ListView.tsx :3133, :3172), so order is the drawn, post-composition list. effectiveFields (:2797-2830) sorts by orderMap.get(id) ?? Infinity; under the seat's sketch a saved [name, stage] with view fieldOrder: [stage, name] sorts straight back to [stage, name], so the drag undoes itself. Under the PR's gate hasColumns(cfg) makes viewComposes false and the drag sticks.

Empty-columns branch — matches the spec text, with a sourcing caveat. view.zod.ts at origin/main, columns docblock :2505-2511 "An EMPTY list declares no projection, so neither of them applies", describe :2519-2521 "An empty list declares no projection, so neither of them applies: which columns show is then left to the renderer (objectui's ListView grid derives the object's default columns)", composition docblock :2657-2665 "Three keys on this schema together build one field list … An EMPTY columns declares no projection, so steps 2 and 3 do not apply". Branch 3 (head :454-459) is exactly that. Caveat: this text landed in spec SOURCE on 2026-09-23 (9dcdb77, objectstack#19598); the installed 17.4.0 dist (objectui's pin ^17.3.0, resolves 17.4.0; origin/main packages/spec/package.json is still 17.4.0) carries only "Fields to hide in this specific view" / "Explicit field display order for this view" and no composition sentence. The PR body cites these as "Spec, ListViewSchema.columns" without saying they are unreleased source. Not false; finding — date and source the quote.

Object-route consistency — the divergence is real, the PR is the side that matches the spec, and it is stated. ObjectView.tsx views memo (head ~:1884-1891): "Apply default columns to any grid-like view that has no explicit columns … v.columns = resolveDefaultColumns()", then the relay carries hiddenFields: (viewDef as any).hiddenFields ?? listSchema.hiddenFields (:2702) and fieldOrder: viewDef.fieldOrder ?? listSchema.fieldOrder (:2719). So a columns: [] view subtracts from and sorts the defaults on the object route, while the spec says steps 2 and 3 do not apply. Pre-existing on ObjectView, not this PR's. The body states it (Acceptance notes, second bullet) and the report lists it with "Carrier: none" — nobody has carded it. Finding: it should be carded against ObjectView (or the spec sentence), not left unowned.

Pins — all six behave as claimed; measured. Worktree at head under the scratch dir, node_modules borrowed from /home/user/objectui-issue-10659 (pnpm store, spec 17.4.0). Lock: first call VERDICT queue-timeout (exit 99), never acquired after 540s, nothing ran or mutated; second call with slot pr-10667-review acquired after 508s, held 55s, VERDICT command-exit 0. The pin mounts the real InterfaceListPage (relative import) with ListView stubbed to capture schema — the posture of ObjectView.fieldOrderRelay-7516.test.tsx :99-105 — so it asserts the relayed schema, not what is drawn. Its @object-ui/react useMetadata mock does reach the page because MetadataProvider.tsx :14/:21 re-exports it from @object-ui/react.

  • HEAD (on-disk blob 00977917bcae): Tests 6 passed (6).
  • BASE file swapped in (blob f3e2e80e0509 = base blob; viewComposes count 0): Tests 3 failed | 3 passed (6), the three FIX, AssertionError: expected undefined to deeply equal [ 'owner' ] / [ 'stage', 'name' ] at :167 / :175 / :184 — the key never arrives, the right reason. PRECEDENCE and both CONTROLs green on base, as the body says. Restore proven: blob 00977917bcae, git diff 0 lines, status clean.
  • ABLATION A (both spread lines deleted; blob 740fff063f76, matching the body's prediction; spread count 0): Tests 3 failed | 3 passed (6), the three FIX. Restore proven as above.
  • ABLATION B (viewComposes && view. to true && view., anchor 2 to 0, replacement 0 to 2, blob 57d64b935e1b): Tests 2 failed | 4 passed (6): PRECEDENCE and the empty-columns CONTROL, expected true to be false on 'hiddenFields' in schema at :198 / :210. Restore proven.
  • Not re-measured by me: the dev's uncommitted "draws" probe. The drawn result follows from effectiveFields at source ([name, owner, stage] minus owner, sorted by [stage, name] gives [stage, name]) and from ListView re-seeding its hiddenFields state from the schema on change (ListView.tsx :1564-1577, JSON-keyed effect), which also covers this page's hollow-view hydration path (viewDef = {...resolvedView, ...hydratedView}, :389-392), where the keys arrive after mount.

Other keys — true, and it leaves the page half fallback-only; finding, not a blocker. Read at head: filter is always concatenated (:435-438); sort page-first (:462); grouping, rowColor, pagination, searchableFields, emptyState unconditional (:479-483); the viz bindings view.X ?? derived (:410-430); userFilters and appearance page-first (:401, :495). None is gated on columns, so the spec's "fallback when the page has no own columns" describes none of the page's other rungs, and the new gate applies that sentence to the two keys alone. It is coherent per composition (the two keys have no page spelling, and the spec declares the three keys as one unit), and the inconsistency for the other keys predates this PR. No published sentence claims the page is fallback-only overall.

Edited header sentence — wording finding, non-blocking. The diff edits :9-11 to "the page REFERENCES one view (interfaceConfig.sourceView) — columns (with the view's hiddenFields / fieldOrder), base filter and sort are inherited, never restated (the iron rule)". "never restated" contradicts the spec's ADR-0047 (revised) sentence and this file's own :432-434 ("the PAGE owns its view metadata. Precedence everywhere: the page's own config → legacy sourceView view") and :455. It was false on base; the PR touched the sentence and kept the false half. Replacement: "the page REFERENCES one view (interfaceConfig.sourceView) as a fallback — its columns (with its hiddenFields / fieldOrder), base filter and sort are inherited unless the page defines its own columns / filterBy / sort (ADR-0047 revised);".

② Semver level

  • '@object-ui/app-shell': patch holds: a behaviour fix on one host; the diff adds a local const viewComposes and two conditional spreads, touches no export, type, schema or accept set. Same level as the precedent .changeset/7516-fieldorder-relay-rung.md (patch). Clause-②: no holds.
  • Changeset, sentence by sentence: true. The composition sentence matches effectiveFields (including "a surviving column it does not list sorts last"); the drag sentence matches :592; the empty-columns sentence matches :454-459 and the spec source. One imprecision, not a falsity: "carried that view's columns, filter and sort but not its hiddenFields or fieldOrder" reads as exhaustive while the page also carried grouping, rowColor, pagination and more; suggested: "carried that view's columns, filter, sort (and its other keys) but not its hiddenFields or fieldOrder".
  • PR body: no sentence is false. "NOT MEASURED when this PR was opened … The seat's report carries their verdict lines" is dated and true — comment 5835253526 carries type-check exit 0 and 23 passed (23) / 362 passed (362), and CI Type Check and all eight Test shards are green on this head. The alias-map, strictObject, onColumnStateChange, fixture and blob-hash claims (00977917bcae / f3e2e80e0509 / 740fff063f76) all reproduce. "only .changeset/7218-rowcolor-host-relay.md names the edited file (per check:changeset-claims)" — I re-ran the gate on 4a3d500fd..b172bf0fb; it names only 7218 (hollow-view-overlay-hydration-pin-5773.md also spells InterfaceListPage.tsx in prose but the gate does not report it; its sentence about the hydration effect is still true, the effect is untouched). H5 holds: no doc, README or content sentence names which source-view keys the page honours. The two ListViewSchema quotes are from unreleased spec source (see ①); should be sourced.
  • 7218-rowcolor-host-relay.md is still true: head :479-481 grouping: view.grouping, rowColor: view.rowColor, pagination: view.pagination, no fence; still present on main.

③ Boundary flags

  • CI on b172bf0fb: 43 check runs, 40 success, 3 skipped (two coverage jobs, dependabot), 0 red. Test (shard 2/8) is success — the objectui#10645 intermittent did not fire; no red check of any kind.
  • git merge-tree --write-tree: against main c659439b4 exit 0 (tree 35dcf65cd); re-run at 18:2xZ against main ed8251189 exit 0 (tree c29aab0e7). Main has 0 commits on the three changed files since merge-base 4a3d500fd. Clean.
  • Open PRs touching InterfaceListPage.tsx: only fix(app-shell): an interface page relays its source view's hiddenFields and fieldOrder (objectui#10638) #10667 itself (scanned twice, 18 then 11 open PRs).
  • Draft: true; assignees: none (card assignee os-elon-musk); head unchanged since 15:33Z; labels tests, package: app-shell. The dev's assign write was refused by the classifier — the seat should apply --assign os-elon-musk and un-draft before merge.
  • Lock contention: one exit-99 call (nothing measured, nothing mutated), then a slotted call acquired after 8m28s; nothing was run unlocked. Worktree removed; borrowed node_modules intact.
  • Side effect of the brief's fetch: origin/main in the shared /home/user/objectui checkout advanced (0c3f70aae to ed8251189); no checkout, edit, stash or commit there.

Implemented-by: claude/issue-10638-interface-list-view-keys
Reviewed-by: session_014mXUNuFomfj24w7s1pZzhN

VERDICT: PASS


Generated by Claude Code

…k; changeset names the other carried keys

Round 2 wording only, no code change. The InterfaceListPage header clause
said the source view's columns, filter and sort are "never restated"; the
page defines its own columns / filterBy / sort and the view is the ADR-0047
(revised) fallback. The changeset's list of carried keys now reads as
non-exhaustive.

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

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3060.3 KB 3104.5 KB
Main entry chunk (gzip) 148.1 KB 350 KB
Entry file index-DtKxlw2f.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.57KB 6.15KB
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.17KB 10.58KB
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.39KB 1.21KB
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) 547.20KB 130.96KB
core (index.js) 9.52KB 3.79KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 223.91KB 62.28KB
fields (index.js) 259.18KB 65.61KB
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.52KB 14.64KB
plugin-charts (index.js) 80.42KB 22.22KB
plugin-chatbot (index.js) 198.36KB 47.20KB
plugin-dashboard (index.js) 133.93KB 35.54KB
plugin-designer (index.js) 216.25KB 44.39KB
plugin-detail (index.js) 232.85KB 61.58KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 152.55KB 39.16KB
plugin-gantt (index.js) 169.71KB 41.93KB
plugin-grid (index.js) 216.70KB 59.29KB
plugin-kanban (index.js) 48.36KB 15.09KB
plugin-list (index.js) 115.82KB 28.76KB
plugin-map (index.js) 23.01KB 7.60KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.55KB 11.99KB
plugin-timeline (index.js) 30.84KB 9.01KB
plugin-tree (index.js) 10.52KB 3.69KB
plugin-view (index.js) 87.77KB 21.99KB
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) 116.21KB 38.14KB
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) 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

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 2a26e30ec16dc95c0154788783770b41fa122c0f

① Derived judgments

Diff shape — as expected. b172bf0fb..2a26e30ec is one commit, 2 files: the changeset (one sentence rewrapped) and InterfaceListPage.tsx header :9-12. Every changed line in the .tsx begins with *; a filter of the diff for non-comment lines returns nothing. Comment-only.

Header clause — two legs true, one leg false. The new text is round 1's replacement applied verbatim. Read against head: columns — true, :455-459 (hasColumns(cfg) wins, else viewComposes gives the view's, else defaults); sort — true, :462 (page-first, else view). filter — false: :435-438 always spreads view.filter and appends cfg.filterBy; the page's filterBy adds to the inherited base filter, it never displaces it. So "base filter … inherited unless the page defines its own … filterBy" is not what the file does. Round 1 recorded exactly this ("filter is always concatenated (:435-438)") and then wrote a replacement that contradicts it; the dev did as asked, the error is round 1's. Against the spec: the 17.4.0 sourceView JSDoc gates the fallback on "no own columns" alone, and the ADR-0047 (revised) comment says "columns, sort and base filter are defined directly here"; neither supports a filterBy-gated fallback. :433-434 ("Precedence everywhere") is itself loose on filter, pre-existing. Replacement for :9-12:

"the page REFERENCES one view (interfaceConfig.sourceView) as a fallback — its columns (with its hiddenFields / fieldOrder) and sort are inherited unless the page defines its own columns / sort; its base filter is always inherited, with the page's filterBy appended (ADR-0047 revised);"

Changeset — true, frontmatter intact. md5 of lines 1-3 is 4e8ce55021fe3b14329c8b4cb3f9f354 at both heads (also of the whole --- block). The only hunk is the rewrap; the edited sentence is true (:480-484, :496-497 carry grouping, rowColor, pagination, searchableFields, emptyState, userFilters and more). The other sentences are byte-identical and still match :455-459, :476-477, :592.

Landing body — spec sourcing correct. 17.4.0 dist (package.json 17.4.0, dist/ui/index.js) contains "Defined directly on the page (no view inheritance)." and "Still honored at runtime as a fallback when the page has no own columns." and contains neither "declares no projection" nor "Three keys on this schema". objectstack origin/main 16c5a33f, packages/spec/src/ui/view.zod.ts :2509 "An EMPTY list declares no projection, so neither of them applies" and :2657 "Three keys on this schema together build one field list"; git log -S names 9dcdb77 (#19598) as the only commit introducing the text. All four attributions in the body are right.

Landing body — Round 2 section true. The three fixes are as described; the md5 claim reproduces; the gate lines agree with the green Changeset Declaration, Changeset Bump Policy, Control Byte Scan, Line Citation Gate, Type Check runs on the head. "fresh origin/main ed8251189 … git merge-tree is clean" was true when written and remains true on 7afc81df3 (below). Acceptance note 1 is true at source (filter :435-438 always; sort :462, userFilters :496 page-first, none columns-gated). Acceptance note 2 matches round 1's ObjectView reading.

Landing body — three wording faults.

  1. The "What changed" header bullet restates the false filter leg. Replacement: "The file header (round 2). It now states sourceView as a fallback: the view's columns (with its hiddenFields / fieldOrder) and sort are inherited unless the page defines its own columns / sort, and its base filter is always inherited with the page's filterBy appended (ADR-0047 revised). The "never restated" wording it replaces was false on base as well." Round 2 item 1 needs no change.
  2. Angle brackets: line 41 (Ablation A) and line 42 (Ablation B) spell four arrows as hyphen plus greater-than sign — anchor x1 to x0, blob 00977917bcae to 740fff063f76, anchor x2 to x0, replacement x0 to x2. Line 42 already uses → for the viewComposes && view. edit. Replace each with →. Present in the live PR body too; round 1 did not flag it.
  3. "23 files across 8 packages": at head, test files naming InterfaceListPage are 23 across 9 packages (app-shell, components, core, data-objectstack, i18n, plugin-calendar, plugin-list, plugin-view, types). Replacement: "(23 files across 9 packages)".

No other changed sentence is false; no unchanged sentence regresses. Closing keyword: only Fixes #10638, line 1.

Disclosed leftover — unversioned, not false. viewComposes comment :444-454 and pin docblock :34-44 quote "declares no projection, so neither of them applies" as "the spec". The sentence exists in spec source at origin/main (view.zod.ts :2509) and the per-view composition is declared there (:2655-2665), so the statements are true of the spec as it stands; they are not true of the released 17.4.0. Code comments, not published text. Unversioned only.

② Semver level

Unchanged from round 1: '@object-ui/app-shell': patch holds; this round moves no code. Clause-②: no holds.

③ Boundary flags

  • CI on 2a26e30ec: 43 check runs, 40 success, 3 skipped (Test (coverage), Test (coverage shard …/4), dependabot), 0 red. Test (shard 2/8) job 108198674719 success; the objectui#10645 intermittent did not fire.
  • git merge-tree --write-tree refs/review/main refs/review/pr-10667 against main 7afc81df3: exit 0, tree aa321be72. Main has 0 commits on the three changed files since merge-base 4a3d500fd.
  • /home/user/objectui/node_modules does not exist; the 17.4.0 dist was read from /home/user/objectui-issue-10615/node_modules/.pnpm/@objectstack+spec@17.4.0_ai@7.0.65_zod@4.4.3_/node_modules/@objectstack/spec (package.json 17.4.0). Not re-measured for the same reason: the lint counts (39 / 39 / 9), type-check exit 0, 362 passed. Consistent with source: eslint.config.js has no parserOptions / projectService; eslint-rules/ has no fs reads.
  • gh is not installed; PR data read via the GitHub MCP read tools only. No GitHub writes.
  • Side effect of the brief's fetch: origin/main in the shared /home/user/objectui advanced ed8251189 to 7afc81df3; no checkout, edit or commit there.
  • PR still draft, no assignee (round 1's note stands).

Implemented-by: claude/issue-10638-interface-list-view-keys
Reviewed-by: session_014mXUNuFomfj24w7s1pZzhN

VERDICT: FAIL

Wording only; the code is unchanged since round 1's PASS. To land: apply the :9-12 replacement in InterfaceListPage.tsx (comment-only commit), and in the landing body apply items 1-3 above.


Generated by Claude Code

…ys inherited

Round 3, comment only. The page's filterBy is appended to the source
view's base filter and never displaces it; only columns (with their
hiddenFields / fieldOrder) and sort fall back to the view.

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

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3060.4 KB 3104.5 KB
Main entry chunk (gzip) 148.2 KB 350 KB
Entry file index-M5hovbhH.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.57KB 6.15KB
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.17KB 10.58KB
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.39KB 1.21KB
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) 547.33KB 130.98KB
core (index.js) 9.52KB 3.79KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 223.91KB 62.28KB
fields (index.js) 259.18KB 65.61KB
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.52KB 14.64KB
plugin-charts (index.js) 80.42KB 22.22KB
plugin-chatbot (index.js) 198.36KB 47.20KB
plugin-dashboard (index.js) 133.85KB 35.54KB
plugin-designer (index.js) 216.25KB 44.39KB
plugin-detail (index.js) 232.85KB 61.58KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 152.55KB 39.16KB
plugin-gantt (index.js) 169.71KB 41.93KB
plugin-grid (index.js) 216.70KB 59.29KB
plugin-kanban (index.js) 48.36KB 15.09KB
plugin-list (index.js) 115.82KB 28.76KB
plugin-map (index.js) 22.85KB 7.58KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.55KB 11.99KB
plugin-timeline (index.js) 30.84KB 9.01KB
plugin-tree (index.js) 10.52KB 3.69KB
plugin-view (index.js) 87.83KB 22.01KB
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) 116.21KB 38.14KB
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) 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

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 106a488031501844479b056b9a7a52502a5c4bfc

① Derived judgments

Diff shape — as expected. 2a26e30ec..106a48803 is one commit (docs(app-shell): interface page header states the base filter is always inherited), one file, InterfaceListPage.tsx :9-13, 4 insertions / 3 deletions. Every changed line begins with *; the non-comment filter of the diff returns 0 lines. Comment-only. The text is round 2's :9-12 replacement applied verbatim (now five lines, :9-13).

Header clause — true on all three legs at head. columns: :456-461 — hasColumns(cfg) wins, else viewComposes gives the view's columns, else defaultColumnsFromObject; hiddenFields / fieldOrder ride only on the viewComposes branch, :477-478. sort: :464 — the page's non-empty sort, else view.sort. Filter: :437-440 — view.filter is always spread, cfg.filterBy appended after it; nothing displaces the base filter. "(ADR-0047 revised)" unchanged.

Landing body — every changed sentence true.

  • "What changed" header bullet: restates the head clause leg by leg, correctly; "(rounds 2 and 3)" is right; "never restated" was false on base 4a3d500fd, which already had hasColumns(cfg) ? cfg.columns (:442-443), cfg.sort first (:449) and cfg.filterBy appended (:436).
  • Arrows: Ablation A and B now spell all four with →; the body contains no < or > at all.
  • "23 files across 9 packages": at head, git grep over *.test.ts(x) / *.spec.ts(x) names 23 files in app-shell (14), plugin-list (2), components, core, data-objectstack, i18n, plugin-calendar, plugin-view, types (1 each) — 9 packages.
  • Acceptance note 2: objectui#10694 exists (open, label finding, filed 18:31Z by the same seat/session from this PR's acceptance note), titled on ObjectView filling default columns into a columns: [] view and then applying its hiddenFields / fieldOrder; sites ObjectView.tsx about :1884-1891, :2702, :2719. It is the empty-columns card.
  • Round 3 section: comment-only — true; check:control-bytes / check:new-line-citations — Control Byte Scan and Line Citation Gate green on head; type-check — Type Check green on head (local exit 0 not re-measured); git merge-tree against origin/main f905090a1 clean — reproduced (below). One citation note: :435-438 is round 2's range and resolves at 2a26e30ec (const filters :436, view.filter :437, cfg.filterBy :438); at this head the same code is :437-440 because the commit added one header line. The sentence attributes the range to the round-2 re-review, so it is not false; flagged below with replacement text.
  • Closing keyword: only Fixes #10638, line 1. objectui#10694 / objectui#7516 / objectstack#19598 are plain references.
  • No unchanged sentence regresses: Round 2 item 1 still describes the head header; "fresh origin/main … has not touched the three files" still holds (0 commits on them since merge-base 4a3d500fd); Acceptance note 1 still matches :437-440, :464, :497.

Round-2 record confirmed on the PR: comment 5837745076 (18:45:14Z), Head-sha 2a26e30ec, VERDICT FAIL; its three body items and the :9-12 replacement are all applied.

② Semver level

Unchanged: '@object-ui/app-shell': patch holds; this round moves no code. Clause-②: no holds.

③ Boundary flags

  • CI on 106a48803: 43 check runs, 40 success, 3 skipped (Test (coverage), Test (coverage shard …/4), dependabot), 0 red, none in progress. Test (shard 2/8) job 108204922527 success; the objectui#10645 intermittent did not fire.
  • git merge-tree --write-tree refs/review/main refs/review/pr-10667 against main f905090a1: exit 0, tree 41d9cc42d. Main has 0 commits on the three changed files since merge-base 4a3d500fd. GitHub reports mergeable_state: clean.
  • Optional body tightening (not a FAIL — the sentence is a true attribution, but the range is one line stale for the head the section names). Replacement for the Round 3 sentence: "The round-2 re-review found the header clause false on its filter leg: the page always spreads the view's filter and appends its own filterBy (:437-440 at this head), so the page never displaces the base filter."
  • Not re-measured locally (no node_modules in /home/user/objectui): the app-shell type-check exit 0; CI Type Check stands in. ObjectView was not re-read this round; Acceptance note 2's reading is round 1's, and objectui#10694's body carries the same sites.
  • gh is not installed; PR, issue and check-run data read via the GitHub MCP read tools only. No GitHub writes, no checkout, no edit.
  • Side effect of the brief's fetch: refs/review/pr-10667 and refs/review/main updated in the shared /home/user/objectui; origin/main there is now f905090a1.
  • PR still draft, no assignee (rounds 1–2 note stands).

Implemented-by: claude/issue-10638-interface-list-view-keys
Reviewed-by: session_014mXUNuFomfj24w7s1pZzhN

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review September 25, 2026 19:04
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Sep 25, 2026
Merged via the queue into main with commit bc97f92 Sep 25, 2026
45 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-10638-interface-list-view-keys branch September 25, 2026 19:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

1 participant