Repository navigation
feat(app-shell)!: ObjectView's props are the declared ConsoleObjectViewProps, not any (objectui#7483) - #11010
Conversation
…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
|
changeset-claim-re-read
|
❌ Console Performance Budget
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:
📦 Bundle Size Report
Size Limits
|
|
|
Contract reviewServed-tier: ① Derived judgmentsThe interface matches the ruling.
The repairs are typing, not escape hatches.
Callers are fine. App-shell's Ratchet edit ( BLOCKING 1 — the pin does not bind the ruled optionality of
Gap: BLOCKING 2 — one changeset sentence is false. The sentence is "A mount that passes the four props the component always read compiles as before." Non-blocking wording. The changeset's "Nothing changes at runtime." overstates slightly: the README. Every sentence was checked against the source:
The block is compiled by Out of diff, non-blocking (follow-up). The customer-published ② Semver level
③ Boundary flagsCI on the head (42 check runs):
Merging:
Governance and PR state:
Dispatch record:
Implemented-by: VERDICT: FAIL To land, two things must change:
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
❌ Console Performance Budget
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:
📦 Bundle Size Report
Size Limits
|
Contract reviewServed-tier: This is an in-seat delta review of fix round 1: ① Derived judgments
② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS |
Fixes #7483
Clause-②: yes — a published component's props type narrows from
anyto 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 review5874389538:_PropsAreTheRuledShapepinsConsoleObjectViewPropsto the ruled shape (names, optionality, value types). It is red with TS2344 underonEdit?:and underobjects?:, and green at head.strictFunctionTypesconsequence foronEditand the number type ofexternalRefreshKey.Bundle Analysisis red becausemainitself is over the eager-closure ceiling (objectui#10996; standing-down comment5874286944). It is not a required check.Follow-up from the review: the customer skill guide's broken
ObjectViewmount is objectui#11011.Claim: 5873302545 · executed by the
domain:uiseat 2 dispatch, sessionhttps://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN(mode:subagent). Base8aa68b1596, headf5cf7121d3.What changed
@object-ui/app-shell'sObjectViewand its inner bodyObjectViewInnerno longer take a bareany. Both are typed with the new exportedConsoleObjectViewProps, with exactly the ruled members:dataSource: DataSource(the published@object-ui/typescontract), 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.ConsoleObjectViewPropsis re-exported as a type from the package entry, besideRecordFormPageProps, through the views barrel. It is not namedObjectViewProps:@object-ui/plugin-viewowns that name for a different shape (objectui#6172, one authority per exported type name). plugin-view is untouched (0-line diff underpackages/plugin-view).packages/app-shell/README.md: the### ObjectViewblock now passes the four props and lists them. The old block mounted the view withdataSourcealone. It compiled, and a verbatim copy threw on first render atobjects.find(...).7483-console-objectview-props.md,'@object-ui/app-shell': minor. It states the narrowing: the props type is declared,onEditandobjectsare 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 typingRe-measured on today's main: 3. That matches the earlier dev report's V6 count (comment 5828686918). There is no cast to
anyand no@ts-expect-error.dataSource.updateViewConfig(...)call inside thesetTimeoutclosure ofpersistViewPatch. The guard at the top of the callback does not narrow into the closure, and the contract declaresupdateViewConfigoptional. Repair: an optional member call,dataSource.updateViewConfig?.(...), which keeps the adapter asthis.persistRuntimeMetadata('view', ...)call.createRuntimeMetadata('view', ...)call.Repair for 2 and 3: the seam declares the capability it probes.
RuntimePersistCtx.dataSourceinruntime-metadata-persistence.tswas a bare Partial ofViewCacheInvalidator, which is a weak type (every member optional). The publishedDataSourcedoes not declareinvalidateViewKeys; only theObjectStackAdapterclass does. So TypeScript refused a typedDataSource.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
objectand a Partial ofViewCacheInvalidator, or null. That still refuses a primitive and a wrongly typedinvalidateViewKeys, 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,ViewCacheInvalidatorand the module name have 0 hits in the builtdist/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 buildPersistedViewBodyinObjectView.overlayPatchOnly.test.ts) located the call site by the literaldataSource.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.tsxsits beside the view and is compiled by app-shell'stsconfig.test.json.--listFilesshows it andObjectView.tsxamong that project's inputs. It is.tsxrather than.tsbecause the refusals are JSX mounts; the precedent isInspectorComboField.naming.types.test.tsx. Its rows:dataSourceXis TS2322;objectsandonEditis TS2739 ("missing the following properties from type 'ConsoleObjectViewProps': objects, onEdit");any, the parameter equalsConsoleObjectViewProps, anddataSourceequalsDataSource.The two codes were read from a directive-free copy of the file, which was removed after the read.
tsc -p tsconfig.test.jsonObjectView.tsxchecked out, restored after)any, no interfaceConsoleObjectViewProps), TS2344 on_ParamNotAny, TS2344 on_DataSourceIsTheContract, TS2578 on both@ts-expect-errordirectivesany, viaablation-replace.mjs)any_ParamNotAny, TS2344 on_ParamIsTheDeclaredProps, TS2578 on both directivesf5cf7121d3pnpm --filter @object-ui/app-shell type-check, VERDICT command-exit 0)Restore after both red legs was proven by blob and diff: the
ObjectView.tsxblob equals the HEAD blobfe60b6d70fcd, andgit diff HEADis empty.The runtime half is
vitest runof the pin: 3/3 passed. The elements are created, never rendered.README block now checks something. A planted
dataSourceXin the README'sObjectViewblock turnspnpm check:doc-snippetsred: 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. UnderConsoleObjectViewPropsit exits 0 with 11 passed (11). Under the originally ruled nameObjectViewProps, as a mutation that was restored, it exits 1 on "ObjectViewProps — a NEW colliding name".ConsoleObjectViewPropshad 0 hits on main before this change.Gates (at head
f5cf7121d3unless stated)pnpm --filter @object-ui/app-shell build: exit 0;distcarriesConsoleObjectViewPropsinindex.d.tsandviews/ObjectView.d.ts.pnpm --filter @object-ui/app-shell type-check, bothtsc --noEmitandtsc -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.pnpm exec vitest runover 84 files, reported as "Test Files 84 passed (84), Tests 2108 passed (2108)". The 84 files are:views/ObjectView, including the three console suites that mock it;ObjectView.tsxby path;runtime-metadata-persistence;ObjectView, both inAppContent, and both pass all four props. TheObjectViewmounted inexamples/byo-backend-consoleand in the console's timeline test is plugin-view's, imported from@object-ui/plugin-view. No caller needed a change.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.mdand7070-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-keysandcheck:handler-key-reads: each exit 0. These are the gates that read the README orObjectView.tsx.node scripts/check-governed-queue-guard.mjs --testover the 8 changed paths: NOT GOVERNED.eslint --no-inline-config --format jsonover the 6 changed ts/tsx files reported 6 files, 0 errors. The population iseslint.config.js, which has noparserOptions.projectorprojectService, so the linting is not type-aware and the diff cannot move a verdict on an untouched file.ObjectView.tsxwarnings went from 172 to 171. The fullpnpm lintis owned by CI.git merge-tree --write-tree: clean against a fresh maind6d8fb9d7band 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 Analysison main isfailureat6650259081andd6d8fb9d7b, failing at step "Check console performance budget", andcancelledat this PR's base.Acceptance notes
ObjectViewInneris not exported, so the pin cannot reach it. Its typing is judged by thesrcleg only, through the one forwarding mount insideObjectView. Setting only the inner parameter back toanywould not turn the pin red.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.mddescribes app-shell'sObjectViewas a wrapper but names no props, so it is not edited.Bundle Analysisis expected to be red for that reason until main's budget or closure moves.Generated by Claude Code