Skip to content

feat(app-shell)!: ObjectView's props are the declared ConsoleObjectViewProps, not any (objectui#7483) - #11010

Merged
objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-7483-console-objectview-props
Sep 28, 2026
Merged

objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-7483-console-objectview-props

Conversation

@objectstack-fleet

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

Copy link
Copy Markdown
Contributor

Fixes #7483
Clause-②: yes — a published component's props type narrows from any to a declared interface.
Ruling-ref: 5809238375 (UI closure batch 2 item 9, letter declare) and 5871194128 (batch 238 item 1, letter B, maintainer 「同意」 2026-09-28T13:47Z)

Fix round 1 (a527bc95ae), following contract review 5874389538:

  • _PropsAreTheRuledShape pins ConsoleObjectViewProps to the ruled shape (names, optionality, value types). It is red with TS2344 under onEdit?: and under objects?:, and green at head.
  • The changeset sentence about which mounts compile now states the strictFunctionTypes consequence for onEdit and the number type of externalRefreshKey.

Bundle Analysis is red because main itself is over the eager-closure ceiling (objectui#10996; standing-down comment 5874286944). It is not a required check.
Follow-up from the review: the customer skill guide's broken ObjectView mount is objectui#11011.

Claim: 5873302545 · executed by the domain:ui seat 2 dispatch, session https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN (mode:subagent). Base 8aa68b1596, head f5cf7121d3.

What changed

  • @object-ui/app-shell's ObjectView and its inner body ObjectViewInner no longer take a bare any. Both are typed with the new exported ConsoleObjectViewProps, with exactly the ruled members:
    • dataSource: DataSource (the published @object-ui/types contract), required;
    • objects: any[], required and kept loose (the object-metadata type prices at 75 diagnostics and is out of this card);
    • onEdit, a function taking a Record of string to unknown and returning void, required;
    • externalRefreshKey?: number.
  • ConsoleObjectViewProps is re-exported as a type from the package entry, beside RecordFormPageProps, through the views barrel. It is not named ObjectViewProps: @object-ui/plugin-view owns that name for a different shape (objectui#6172, one authority per exported type name). plugin-view is untouched (0-line diff under packages/plugin-view).
  • packages/app-shell/README.md: the ### ObjectView block now passes the four props and lists them. The old block mounted the view with dataSource alone. It compiled, and a verbatim copy threw on first render at objects.find(...).
  • Changeset 7483-console-objectview-props.md, '@object-ui/app-shell': minor. It states the narrowing: the props type is declared, onEdit and objects are now required at the type level, and a misspelled prop is a compile error.

Nothing changes at runtime except the two bytes of one optional call, listed next.

Diagnostics raised by the typed dataSource, each repaired by typing

Re-measured on today's main: 3. That matches the earlier dev report's V6 count (comment 5828686918). There is no cast to any and no @ts-expect-error.

  1. TS2722, "Cannot invoke an object which is possibly 'undefined'". This is the debounced dataSource.updateViewConfig(...) call inside the setTimeout closure of persistViewPatch. The guard at the top of the callback does not narrow into the closure, and the contract declares updateViewConfig optional. Repair: an optional member call, dataSource.updateViewConfig?.(...), which keeps the adapter as this.
  2. TS2559, "Type 'DataSource of any' has no properties in common with type 'Partial of ViewCacheInvalidator'". This is at the view-config save's persistRuntimeMetadata('view', ...) call.
  3. TS2559, the same message, at the create-view flow's createRuntimeMetadata('view', ...) call.

Repair for 2 and 3: the seam declares the capability it probes. RuntimePersistCtx.dataSource in runtime-metadata-persistence.ts was a bare Partial of ViewCacheInvalidator, which is a weak type (every member optional). The published DataSource does not declare invalidateViewKeys; only the ObjectStackAdapter class does. So TypeScript refused a typed DataSource.

The seam is now declared as what it really takes: the host's adapter object, probed for that one capability. It is written as the intersection of object and a Partial of ViewCacheInvalidator, or null. That still refuses a primitive and a wrongly typed invalidateViewKeys, which was probed in isolation before the edit. It admits the partial test doubles the seam's own suites pass. The type is not reachable from the package entry: RuntimePersistCtx, ViewCacheInvalidator and the module name have 0 hits in the built dist/index.d.ts.

The optional call changes the source text a source-level ratchet reads. The objectui#5233 ratchet test (the toolbar write routes its body through buildPersistedViewBody in ObjectView.overlayPatchOnly.test.ts) located the call site by the literal dataSource.updateViewConfig(. Its vacuous-pass guard fired, 1 failed. Its pattern now accepts both spellings of the same call and its guard is unchanged: 17/17 green.

Pins: red on base, green on head

ObjectView.consoleProps.types.test.tsx sits beside the view and is compiled by app-shell's tsconfig.test.json. --listFiles shows it and ObjectView.tsx among that project's inputs. It is .tsx rather than .ts because the refusals are JSX mounts; the precedent is InspectorComboField.naming.types.test.tsx. Its rows:

  • the misspelled prop dataSourceX is TS2322;
  • a mount that omits objects and onEdit is TS2739 ("missing the following properties from type 'ConsoleObjectViewProps': objects, onEdit");
  • the four-prop control compiles;
  • type equations: the parameter is not any, the parameter equals ConsoleObjectViewProps, and dataSource equals DataSource.

The two codes were read from a directive-free copy of the file, which was removed after the read.

leg what was on disk tsc -p tsconfig.test.json
base (base ObjectView.tsx checked out, restored after) parameter any, no interface exit 2: TS2305 (no exported member ConsoleObjectViewProps), TS2344 on _ParamNotAny, TS2344 on _DataSourceIsTheContract, TS2578 on both @ts-expect-error directives
mutation (head, only the exported parameter set back to any, via ablation-replace.mjs) interface present, parameter any exit 2: TS2344 on _ParamNotAny, TS2344 on _ParamIsTheDeclaredProps, TS2578 on both directives
head f5cf7121d3 as committed exit 0 (inside pnpm --filter @object-ui/app-shell type-check, VERDICT command-exit 0)

Restore after both red legs was proven by blob and diff: the ObjectView.tsx blob equals the HEAD blob fe60b6d70fcd, and git diff HEAD is empty.

The runtime half is vitest run of the pin: 3/3 passed. The elements are created, never rendered.

README block now checks something. A planted dataSourceX in the README's ObjectView block turns pnpm check:doc-snippets red: exit 1, "679 of 679 block(s) judged, 1 failed", TS2322 "Property 'dataSourceX' does not exist". The restore was proven by blob. The card measured the same mutation green on the old block.

One-authority gate control. The gate is scripts/__tests__/one-authority-per-exported-name-6273.test.ts. Under ConsoleObjectViewProps it exits 0 with 11 passed (11). Under the originally ruled name ObjectViewProps, as a mutation that was restored, it exits 1 on "ObjectViewProps — a NEW colliding name". ConsoleObjectViewProps had 0 hits on main before this change.

Gates (at head f5cf7121d3 unless stated)

  • pnpm --filter @object-ui/app-shell build: exit 0; dist carries ConsoleObjectViewProps in index.d.ts and views/ObjectView.d.ts.
  • pnpm --filter @object-ui/app-shell type-check, both tsc --noEmit and tsc -p tsconfig.test.json: VERDICT command-exit 0. The base measurement was 0 and 0.
  • pnpm --filter @object-ui/console type-check, with the console dependency closure built first: VERDICT command-exit 0.
  • Tests: pnpm exec vitest run over 84 files, reported as "Test Files 84 passed (84), Tests 2108 passed (2108)". The 84 files are:
    • every file that imports app-shell's views/ObjectView, including the three console suites that mock it;
    • every test that reads ObjectView.tsx by path;
    • every importer of runtime-metadata-persistence;
    • the new pin.
  • In-repo callers: git grep finds exactly two mounts of app-shell's ObjectView, both in AppContent, and both pass all four props. The ObjectView mounted in examples/byo-backend-console and in the console's timeline test is plugin-view's, imported from @object-ui/plugin-view. No caller needed a change.
  • One-authority gate: exit 0, 11/11 (see above).
  • pnpm check:doc-snippets: exit 0, "Semantic phase: 679 of 679 block(s) judged, 0 failed". It was first run before the scoped build it asks for and answered PRECONDITION NOT MET (exit 2); that run is not a measurement.
  • node scripts/check-changeset-presence.mjs: exit 0. It reports "6 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)".
  • node scripts/check-changeset-no-major.mjs: exit 0.
  • node scripts/check-changeset-fixed.mjs: exit 0.
  • pnpm check:changeset-claims: exit 0 (report-only). It names three pending changesets that cite files this change touches: 6238-object-payload-enabled.md, 6559-expression-user-input-contract.md and 7070-no-invented-gantt-date-fields.md. Each paragraph was re-read and is still true.
  • pnpm check:pending-changeset-literals: exit 0.
  • pnpm check:new-line-citations: "0 new citation(s)", exit 0.
  • pnpm check:control-bytes: exit 0.
  • check:readme-exports, check:doc-fences, check:doc-examples, check:docs-route-closure, check:icon-record-names, check:i18n-keys and check:handler-key-reads: each exit 0. These are the gates that read the README or ObjectView.tsx.
  • node scripts/check-governed-queue-guard.mjs --test over the 8 changed paths: NOT GOVERNED.
  • Targeted lint: eslint --no-inline-config --format json over the 6 changed ts/tsx files reported 6 files, 0 errors. The population is eslint.config.js, which has no parserOptions.project or projectService, so the linting is not type-aware and the diff cannot move a verdict on an untouched file. ObjectView.tsx warnings went from 172 to 171. The full pnpm lint is owned by CI.
  • git merge-tree --write-tree: clean against a fresh main d6d8fb9d7b and clean against the head of PR objectui#10990, d3747ac005, which also edits the entry. The merged entry keeps both additions.
  • pnpm check:eager-closure: exit 1, and exit 1 on the base too. The local console build reads 3,179,021 gzipped bytes at head against a base of 3,179,020. The base was measured with the four base app-shell sources on disk, then restored. The budget is 3,179,000 bytes. So main is already 20 bytes over in this container, and this PR adds +1 gzip byte (+2 raw), the ?. of the optional call. The dispatch's 3104.0 KB reading does not reproduce on today's main. On CI, Bundle Analysis on main is failure at 6650259081 and d6d8fb9d7b, failing at step "Check console performance budget", and cancelled at this PR's base.

Acceptance notes

  • ObjectViewInner is not exported, so the pin cannot reach it. Its typing is judged by the src leg only, through the one forwarding mount inside ObjectView. Setting only the inner parameter back to any would not turn the pin red.
  • The claim's file surface did not name three of the touched files; each is a direct consequence of the ruled repairs:
    • runtime-metadata-persistence.ts, where the seam's declaration lives;
    • views/index.ts, the barrel the entry re-exports through;
    • ObjectView.overlayPatchOnly.test.ts, the ratchet pattern.
  • content/docs/guide/console-architecture.md describes app-shell's ObjectView as a wrapper but names no props, so it is not edited.
  • The eager-closure red is pre-existing on main, as the reading above shows. This PR's Bundle Analysis is expected to be red for that reason until main's budget or closure moves.

Generated by Claude Code

…tViewProps

ObjectView and ObjectViewInner took a bare `any`, which erased the prop
names: a misspelled prop compiled clean. Both now take the exported
ConsoleObjectViewProps (dataSource: DataSource, objects, onEdit required,
externalRefreshKey optional).

The typed dataSource raised three in-file diagnostics, repaired by typing:
the debounced updateViewConfig call is an optional member call, and the
runtime-metadata seam declares its dataSource as an adapter object probed
for invalidateViewKeys instead of a weak Partial.

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

Re-exports ConsoleObjectViewProps as a type from the package entry, beside
RecordFormPageProps. Adds a tsc-driven pin compiled by tsconfig.test.json:
a misspelled dataSourceX is TS2322, a mount without objects and onEdit is
TS2739, and the four-prop control compiles. The README's ObjectView block
now passes the four props, so check:doc-snippets judges a mount that runs.

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

The toolbar write is now `dataSource.updateViewConfig?.(...)`, so the
source-level ratchet that locates the call site accepts both spellings;
its vacuous-pass guard is unchanged.

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

github-actions Bot commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

changeset-claim-re-read

⚠️ 3 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/6238-object-payload-enabled.md

  • names packages/app-shell/src/index.ts → packages/app-shell/src/index.ts — edited by this change

    No published type changed. ObjectMetadataPayload is exported from its module but that module is not re-exported by packages/app-shell/src/index.ts, the package's only entry, so the removed enabled?: boolean was never on the published surface and no **/src/index.ts is touched. What consumers can observe is behaviour: MetadataService is reachable through the published useMetadataService() hook, both method signatures are unchanged (PromiseANGLE-BRACKETS(void)), and the HTTP request they issue changes from a PUT with a body to a DELETE.

.changeset/6559-expression-user-input-contract.md

  • names packages/app-shell/src/index.ts → packages/app-shell/src/index.ts — edited by this change

    WHAT BREAKS, AND FOR WHOM. This tightens a signature published from the package entry (packages/app-shell/src/index.ts), so an external caller that passes an unchecked or under-declared value stops compiling on upgrade. That is accepted (maintainer ruling 2026-08-27, option A): the only calls it refuses are calls that were never conformant with the contract the module already declared. It ships as minor, not major — objectui's major tracks @objectstack's, so its own breaking changes ship as a minor with the break written down (scripts/check-changeset-no-major.mjs). ⛔ Keeping unknown and ⛔ adding a second, wider entry point were both declined.

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

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

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

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.

Angle-bracketed names in the quoted prose above are rewritten as ANGLE-BRACKETS(name): GitHub deletes tag-shaped fragments from a stored body, and a quote that silently loses the identifier it is about is worse than a visible repair.

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

@github-actions

Copy link
Copy Markdown
Contributor

❌ Console Performance Budget

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

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.

Which half objected:

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

📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.58KB 6.17KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.22KB 10.61KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.17KB 5.40KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.72KB 2.24KB
auth (SocialSignInButtons.js) 9.70KB 3.93KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.70KB 10.94KB
auth (createAuthenticatedFetch.js) 8.52KB 3.45KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.13KB 7.95KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 558.92KB 133.97KB
core (index.js) 9.93KB 3.94KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 227.62KB 63.16KB
fields (index.js) 261.01KB 66.28KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.24KB 2.27KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.40KB 12.91KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.35KB 9.18KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 39.32KB 11.09KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.53KB 4.89KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.25KB 2.17KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.01KB 3.93KB
plugin-calendar (index.js) 52.39KB 14.98KB
plugin-charts (index.js) 84.09KB 22.93KB
plugin-chatbot (index.js) 198.22KB 46.97KB
plugin-dashboard (index.js) 137.56KB 36.67KB
plugin-designer (index.js) 215.78KB 44.42KB
plugin-detail (index.js) 233.49KB 61.79KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 162.67KB 41.95KB
plugin-gantt (index.js) 170.35KB 42.19KB
plugin-grid (index.js) 228.33KB 62.59KB
plugin-kanban (index.js) 48.43KB 15.11KB
plugin-list (index.js) 115.86KB 28.64KB
plugin-map (index.js) 22.90KB 7.62KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.17KB 12.20KB
plugin-timeline (index.js) 31.07KB 9.15KB
plugin-tree (index.js) 11.21KB 3.89KB
plugin-view (index.js) 89.44KB 22.41KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.81KB 3.58KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 119.16KB 39.05KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.03KB 1.86KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.78KB 2.09KB
sdui-parser (codegen.js) 7.50KB 3.05KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 6.16KB 2.71KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 18.27KB 6.22KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 3.83KB 1.49KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.26KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 17.15KB 6.32KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

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

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

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

  • main itself is over the console eager-closure ceiling: about 17 to 20 B over the 3,179,000-byte budget, and main's own Bundle Analysis push runs have been red since 6650259081. This is tracked on objectui#10996, which seat 4 measured at 16:01Z.
  • This PR's own delta is +1 gzip byte, the ?. of the ruled optional call updateViewConfig?.(. The dev measured it on a console build at base and at head.
  • No fix exists yet. Bringing main back under the budget, by trimming code or moving the line, is objectui#10996's to decide.
  • Bundle Analysis is not in the repository's required set, so this PR lands on its required checks and contract review. No re-run: it would read the same main.

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

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: f5cf7121d3662317ee6cd8791404c3949f0ea6b7

① Derived judgments

The interface matches the ruling.

  • export interface ConsoleObjectViewProps is in views/ObjectView.tsx. Its members match 5871194128 exactly:
    • dataSource: DataSource, required, imported as a type from @object-ui/types. That package is a runtime dependencies entry of app-shell, so the emitted .d.ts resolves for consumers.
    • objects: any[], required.
    • onEdit: (record: Record of string to unknown) => void, required.
    • externalRefreshKey?: number.
  • Both functions are typed with it. ObjectViewInner really is typed: its parameter is ConsoleObjectViewProps, not any.
  • The type is exported only as a type: export type { ConsoleObjectViewProps } in views/index.ts, then added to the entry's existing export type { … } list beside RecordFormPageProps. There is no new runtime export, and ObjectViewInner stays unexported.
  • plugin-view is untouched.
  • The package exports map has only . and ./styles.css. RuntimePersistCtx is not reachable from ., and the dev reports 0 hits in dist/index.d.ts. So the only change to the published .d.ts is ObjectView's parameter type and the new interface.

The repairs are typing, not escape hatches.

  • TS2722. The fix is dataSource.updateViewConfig?.(…). The guard if (!dataSource?.updateViewConfig …) return reads the same captured dataSource binding that the 300 ms closure calls. The new no-op path (where it used to throw) can only happen if an adapter loses its method inside the debounce window. The behaviour is preserved in practice, and this does not need its own disclosure.
  • The two TS2559s. (object & Partial OF ViewCacheInvalidator) | null is truthful:
    • The seam's only use is typeof ctx.dataSource?.invalidateViewKeys === 'function' followed by .call.
    • All four callers pass something the new type admits: ObjectView (DataSource), ObjectDataPage (any props), ReportView (DataSource or undefined) and RuntimeDraftBar (any).
    • The intersection still refuses a primitive and a wrongly typed invalidateViewKeys.
  • There is no as any and no @ts-expect-error in source.

Callers are fine. App-shell's ObjectView has exactly two mounts, both in app-shell's own console/AppContent.tsx. Both pass all four props, and handleEdit is (record: any). examples/byo-backend-console mounts plugin-view's ObjectView. No caller needed a change, and none relied on any for something the interface refuses.

Ratchet edit (overlayPatchOnly): not weakened. ObjectView.tsx has one updateViewConfig call site. The pattern still requires buildPersistedViewBody( in the call body and refuses the ...baseViewDef spread, and its vacuous-pass guard is unchanged. The unboundAdapterCall ratchet is not affected by the change.

BLOCKING 1 — the pin does not bind the ruled optionality of onEdit or objects, or the TS2739 it claims.

  • @ts-expect-error is satisfied by any error, whatever its code. The mount ObjectView dataSource={DATA_SOURCE} errors whenever EITHER member is required.
  • Two mutations stay green, with every other assertion still green:
    • onEdit?: (the exact alternative 5871194128 priced and rejected, "REQUIRED … optional costs 2");
    • objects?:.
    • Under either one, the line becomes TS2741, which consumes the directive. _ParamIsTheDeclaredProps compares the interface with itself, and the control still compiles.
  • The dataSourceX row replaces dataSource, so an index signature added to the interface would also leave it green, through TS2741 on the missing dataSource.
  • The value types of onEdit, objects and externalRefreshKey are not pinned at all. Only dataSource is (_DataSourceIsTheContract).
  • The pin is red on base by construction (TS2305) and red under the any mutation, so those claims hold. It is simply not a pin of the ruled shape.
  • Required fix: add to ObjectView.consoleProps.types.test.tsx:
    /** The ruled shape, member by member: names, optionality, value types (5871194128). */
    type _PropsAreTheRuledShape = Assert OF Equal OF (ConsoleObjectViewProps, {
      dataSource: DataSource;
      objects: any[];
      onEdit: (record: Record OF (string, unknown)) => void;
      externalRefreshKey?: number;
    })
    
    Write it in real generic syntax. Quote a red leg with onEdit?: (TS2344 on this line) and then green at head.

Gap: ObjectViewInner cannot be reached by the pin. Reverting only the inner parameter to any would stay green. The gap is disclosed in the PR's Acceptance notes and in the dev report's deviations. It is acceptable: the function is unexported and the ruling did not require pinning it.

BLOCKING 2 — one changeset sentence is false. The sentence is "A mount that passes the four props the component always read compiles as before." onEdit is a function-typed property, so strictFunctionTypes checks it contravariantly. A consumer handler typed (record: Account) => void, where Account has required fields, is now refused (TS2322). The same goes for a non-number externalRefreshKey. Replacement:
Replacement: A mount that passes the four props with values of the declared types compiles as before. An onEdit handler whose parameter is a narrower record type (for example an interface with required fields) is now refused under strictFunctionTypes and must accept Record<string, unknown>, and externalRefreshKey must be a number.

Non-blocking wording. The changeset's "Nothing changes at runtime." overstates slightly: the ?. is a runtime byte, though it sits behind an existing guard. Suggested: "Nothing observable changes at runtime; the one runtime byte is an optional call behind an existing guard." The PR body already says this accurately.

README. Every sentence was checked against the source:

  • the objects.find resolution and the "object not found" state;
  • externalRefreshKey summed into refreshKey;
  • onEdit called with the record.

The block is compiled by check:doc-snippets, and the dev's ablation shows it bites.

Out of diff, non-blocking (follow-up). The customer-published skills/objectui/guides/project-setup.md imports ObjectView from @object-ui/app-shell and mounts ObjectView objectName="contact" with no router. This is the card's own defect class: objectName is not a prop, and the three required props are missing. The mount was already broken at runtime on base and now fails to compile. No gate covers skills/objectui/**. Recommend filing it on the card rather than widening this PR.

② Semver level

'@object-ui/app-shell': minor is correct, and Clause-②: yes is right.

  • The change adds a new exported type name.
  • It narrows a published parameter from any. The narrowing is spelled out in the changeset body, per the fixed-group rule in check-changeset-no-major.mjs (objectui's major is pinned to @objectstack's; breaks ship as minor with the body spelling them out).
  • The nearest precedents (the SchemaRenderer and DashboardRenderer/ListView props-erasure repairs) landed as patch, but they restored an already-declared interface and added no export, so they do not argue for a lower level here.
  • Not major. Changeset Bump Policy and Changeset Fixed Group Check are success.
  • Changeset Declaration is success.

③ Boundary flags

CI on the head (42 check runs):

  • Required, success: Lint, Type Check, Build & E2E, Build Docs, Changeset Declaration, Governed Surface Queue Guard, Spec Main Shape Gate, and Test shards 1, 2, 3, 5, 6, 7 and 8 of 8.
  • Pending: Test (shard 4/8) was still in progress at 16:36Z. That is not final; re-check before landing.
  • Test (dist pins): success.
  • All other checks: success, except these:
    • skipped: Test (coverage), the coverage shards and dependabot;
    • Bundle Analysis: failure, expected (below).
  • Bundle Analysis is not required. Its report shows an eager closure of 3104.5 KB against a 3104.5 KB budget, with "Aggregate closure ceiling ❌ over its ceiling" and every other half ✅. That is the pre-existing overage on main tracked on objectui#10996. CI's KB rounding cannot isolate the PR's own delta. The dev's byte-level reading (3,179,020 on base, 3,179,021 at head, +1 gzip byte for the ?.) is consistent with that report and is not this PR's failure.

Merging:

  • git merge-tree --write-tree against current main 2049b03df7 is clean (tree 332eb5d6d2). The 4 commits main gained since base 8aa68b1596 touch none of the PR's files. GitHub reports mergeable_state behind.
  • Against PR objectui#10990's head d3747ac005, the one other open PR on these files (it touches packages/app-shell/src/index.ts), merge-tree is also clean.

Governance and PR state:

  • check-governed-merges: 0 of 8 paths governed, "NOT governed"; 221 changed lines, under the human-merge threshold.
  • The PR is a draft, the assignee is os-elon-musk, and needs:contract-review is applied.

Dispatch record:

  • The claim 5873302545 is the earliest live claim; 5828070713 is spent per the ruling.
  • Both rulings and the claim are carried on the Ruling-ref: line.
  • Deviations are disclosed in the PR body and in the newest os-dev-report 5874218979:
    • the pin is .tsx rather than .ts, justified by the JSX mounts and the InspectorComboField.naming.types.test.tsx precedent;
    • three files outside the claim's file surface;
    • the eager-closure reading;
    • the ObjectViewInner gap.

Implemented-by: claude/issue-7483-console-objectview-props
Reviewed-by: session_014mXUNuFomfj24w7s1pZzhN

VERDICT: FAIL

To land, two things must change:

  1. Add _PropsAreTheRuledShape to the pin, with a quoted red leg under onEdit?: and green at head.
  2. Replace the changeset sentence "A mount that passes the four props the component always read compiles as before." with the replacement text in ①.

Everything else passes. Shard 4/8 must finish green before landing.

…changeset prose

Review round 1. The @ts-expect-error rows accept any error code, so making
onEdit or objects optional stayed green. A type equation now binds the
ruled shape member by member: names, optionality and value types.

The changeset no longer says every four-prop mount compiles as before: an
onEdit handler typed with a narrower record parameter is refused under
strictFunctionTypes, and externalRefreshKey must be a number. The runtime
sentence now names the one optional-call byte. Frontmatter unchanged.

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, 330 chunks) 3104.5 KB 3104.5 KB
Main entry chunk (gzip) 149.8 KB 350 KB
Entry file index-Dlp8k5SA.js —
Status FAIL —

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.

Which half objected:

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

📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.58KB 6.17KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.22KB 10.61KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.17KB 5.40KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.72KB 2.24KB
auth (SocialSignInButtons.js) 9.70KB 3.93KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.70KB 10.94KB
auth (createAuthenticatedFetch.js) 8.52KB 3.45KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.13KB 7.95KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 558.92KB 133.97KB
core (index.js) 9.93KB 3.94KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 227.62KB 63.16KB
fields (index.js) 261.01KB 66.28KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.24KB 2.27KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.40KB 12.91KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.35KB 9.18KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 39.32KB 11.09KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.53KB 4.89KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.25KB 2.17KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.01KB 3.93KB
plugin-calendar (index.js) 52.39KB 14.98KB
plugin-charts (index.js) 84.09KB 22.93KB
plugin-chatbot (index.js) 198.22KB 46.97KB
plugin-dashboard (index.js) 137.56KB 36.67KB
plugin-designer (index.js) 215.78KB 44.42KB
plugin-detail (index.js) 233.49KB 61.79KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 162.67KB 41.95KB
plugin-gantt (index.js) 170.35KB 42.19KB
plugin-grid (index.js) 228.33KB 62.59KB
plugin-kanban (index.js) 48.43KB 15.11KB
plugin-list (index.js) 115.86KB 28.64KB
plugin-map (index.js) 22.90KB 7.62KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.17KB 12.20KB
plugin-timeline (index.js) 31.07KB 9.15KB
plugin-tree (index.js) 11.21KB 3.89KB
plugin-view (index.js) 89.44KB 22.41KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.81KB 3.58KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 119.16KB 39.05KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.03KB 1.86KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.78KB 2.09KB
sdui-parser (codegen.js) 7.50KB 3.05KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 6.16KB 2.71KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 18.27KB 6.22KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 3.83KB 1.49KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.26KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 17.15KB 6.32KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

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

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: a527bc95aeb87f2b704027e2de772c4999f9c925

This is an in-seat delta review of fix round 1: f5cf7121d3 to a527bc95ae, one commit. I checked it against record 5874389538 (FAIL: the pin does not bind the ruled shape, and one changeset sentence is false; the interface, the repairs, the callers, the ratchet edit, the README and the semver passed).

① Derived judgments

  • Scope: 2 files, +26 −3: the pin file and the changeset. ObjectView.tsx, the seam, the entry and the README are byte-identical to the reviewed head.

  • BLOCKING 1 is fixed. The pin gains _PropsAreTheRuledShape, which asserts Equal between ConsoleObjectViewProps and the literal ruled shape:

    • dataSource: DataSource;
    • objects: any[];
    • onEdit: (record: Record of string to unknown) => void;
    • externalRefreshKey?: number.
    • Its docblock says why the @ts-expect-error rows alone do not bind the shape.
    • The dev's red legs, each through ablation-replace and restored with the blob equal to HEAD:
      • onEdit?: gives TS2344 on the Equal argument (exit 2);
      • objects?: gives the same TS2344.
    • Green at head: both tsc legs have 0 errors, and the pin runs 3 of 3.
    • A value-type or optionality change, or an added index signature, now fails the equation.
  • BLOCKING 2 is fixed. The changeset now says:

    • a mount compiles as before when its values have the declared types;
    • an onEdit handler with a narrower record parameter is refused under strictFunctionTypes;
    • externalRefreshKey must be a number.

    This is the reviewer's text verbatim.

  • The non-blocking runtime wording is taken: "Nothing observable changes at runtime; the one runtime byte is an optional call behind an existing guard".

  • The frontmatter is byte-identical.

  • The commit trailers are the model-free Claude pair.

② Semver level

minor and Clause-②: yes hold, unchanged from 5874389538.

③ Boundary flags

  • CI: this record posts only after all eight REQUIRED checks are final and green at a527bc95ae: Lint, Type Check, Build & E2E, Build Docs, Changeset Declaration, Test, Spec Main Shape Gate and Governed Surface Queue Guard. Bundle Analysis (not required) is red for the pre-existing overage on main (objectui#10996, standing-down comment 5874286944). This PR's own delta is +1 gzip byte.
  • Merge-tree: the landing queue re-runs git merge-tree against current main.
  • Carried: the customer skill guide's broken mount is objectui#11011.
  • Landing: ready, auto-merge, and needs:contract-review removed.

Implemented-by: claude/issue-7483-console-objectview-props
Reviewed-by: session_014mXUNuFomfj24w7s1pZzhN

VERDICT: PASS

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review September 28, 2026 16:54
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Sep 28, 2026
Merged via the queue into main with commit ad0310f Sep 28, 2026
44 of 45 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-7483-console-objectview-props branch September 28, 2026 17:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation package: app-shell tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

finding(app-shell): ObjectView's entire props surface is typed any — a misspelled prop on a public route-level view raises nothing

2 participants