Repository navigation
fix(plugin-view): route 2 reads a named view's fieldOrder, navigation and inlineEdit (objectui#10885, member 4) - #10967
Conversation
… and inlineEdit (objectui#10885) `navigationConfig` reads the active named view's `navigation` first, whole object, then the node's, so the row-click handler both routes hand down, the record surface and the drawer width follow it. Route 2 puts its projection in the named view's `fieldOrder` the way `ListView` does, and hands `ObjectGrid` the named view's `inlineEdit` as `editable`, which the grid still ANDs with the object's inline-edit grant. The objectui#5097 fence is untouched. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN
…d fieldOrder (objectui#10885) The row click on route 2 and on the delegation, and the drawer width, follow a named view's `navigation`; a named `inlineEdit` opens the real grid's in-cell editor only where the object grants it; and route 2's `fieldOrder` projection is compared with `ListView`'s on the same named view. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN
…lineEdit (objectui#10885) The README and the plugin-view docs page stop saying `navigation` is not read off a named view on the registered renderer, and say what the path reads now. The pending member-1 changeset gets a dated note for the same sentence, its frontmatter untouched, and this member gets its own patch changeset. The objectui#7779 read-text pin follows the `navigationConfig` line, which still reads the node's `navigation` as the fallback. 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. 📦 Bundle Size Report
Size Limits
|
Contract reviewServed-tier: ① Derived judgmentsThe code is sound. The prose needs five fixes (listed at the end of this section).
The objectui#7779
Pins
Prose: five fixes before landing (wording only, no figure or pin moves)
② Semver level
③ Boundary flags
Implemented-by: VERDICT: FAIL The five wording fixes in ① ( |
…ed navigation (objectui#10885) Contract-review fix round 1, wording only. `label` and `data` are not handed to the grid on route 2, by ruling; `data` waits on objectui#10971. A named view's `navigation` moves the surface and width of the record, create and edit forms, not only the row click, and a host `onRowClick` passed to `ObjectView` still wins on both paths. The member-1 pin's docblock names what member 4 now reads. 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. 📦 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 |
…nvalidation (objectui#10887) (objectstack-ai#10973) Fixes objectstack-ai#10887 Clause-②: yes — one identifier, `useDataInvalidation`, joins the published author scope of `kind: 'react'` pages, and `content/docs/guide/react-pages.md` teaches it. **Why a closing keyword.** The card has three members. Members 1 and 2 landed in PR objectstack-ai#10898 (squash `9f0c84a44`). This PR lands member 3, ruled A (ruling `5864668610`), so all three are in once it merges. The four review notes from the members 1 and 2 round ride this round: (a) and (d) are text and are corrected here; (b) and (c) need a behaviour change, so they are reported as findings and not changed here (see Acceptance notes). The objectstack half of the ruling (the docs page, the skill and the showcase pages) is carried by objectstack-ai/objectstack#20395. The `Blocked-by` line on objectui#10519 is the seat's to update; this PR does not touch that card. ## What changed - **`@object-ui/components`, the react-page renderer.** `ReactKindPage` injects `useDataInvalidation` from `@object-ui/react` into the author scope, beside `useAdapter`. It is the same hook `ListView` reads to refresh its rows: `useDataInvalidation('object')` returns a nonce that moves when the bus reports a write to that object, or `'*'`. A page names it in the dependency list of the effect that reads through `useAdapter`, and that read re-runs in place. The hook is a module-level function, and the scope memo still depends on the schema and the adapter only, so the scope keeps its identity and no page recompiles or loses state. The file header's scope list gains the entry. - **The react-pages guide.** The scope table gains a `useDataInvalidation` row. The Live data example reads `const changed = useDataInvalidation('showcase_project')` and names `changed` in the effect's dependency array, followed by one sentence on why: the nonce moves each time the bus reports a write to that object, so the effect reads again in place without remounting the page. - **Changeset** `10887-react-page-data-invalidation`: `minor` for `@object-ui/components`, `Clause-②: yes`. - **No new dependency edge.** `useDataInvalidation` is exported by `@object-ui/react`, which `@object-ui/components` already depends on and already imports `useAdapter` from. ## Review notes from the members 1 and 2 round (`5864212493`) | note | what it said | here | | --- | --- | --- | | (a) | the plugin-view changeset's "switches off that view's own bus reader" is imprecise for a gantt handed zero rows | **Corrected (text).** The pending changeset `10887-object-view-non-grid-bus-reader` now names the exception: a gantt handed zero rows still queries for itself and keeps its own bus reader (objectui#7333). Frontmatter byte-identical (the first three lines hash the same before and after); the file has not shipped (no CHANGELOG carries its text), so it is corrected in place. The same imprecision in `ObjectView`'s member 1 comment is corrected too. | | (b) | `fetchDrawsView` is a deny-list, so a host `views` entry the renderer does not draw adds one unused read | **Not changed: behaviour.** An allow-list changes which views subscribe. Reported as a finding. | | (c) | a calendar re-read flashes its loading placeholder | **Not changed: behaviour.** `ObjectView`'s non-grid fetch sets `loading` on every run and passes it to the view, and `ObjectCalendar` shows it; a silent re-read changes behaviour. Reported as a finding. | | (d) | the `identityKey` comment is incomplete | **Corrected (comment).** It now says the non-grid fetch re-reads when `refreshKey` moves or when the bus reports a change to the object (objectui#10887), that a gantt handed rows draws them while a gantt handed zero rows queries for itself (objectui#7333), and that `tree` reads the bus itself (objectui#10778). | Both comment edits sit inside `ObjectView`'s component body, so neither reaches a published `.d.ts`; no plugin-view surface moves and the plugin-view changeset keeps its `patch`. ## Pin, through a real react page `packages/components/src/__tests__/react-page-invalidation.test.tsx`. The page is compiled from source by `@object-ui/react-runtime` inside the real `ReactKindPage`, dispatched by the real `PageRenderer` for `type: 'home'`; the events go through the real bus (`notifyDataChanged`). Only the adapter is a stand-in, so reads can be counted. The page source is the guide's Live data pattern with the nonce. - One record-scoped bus event on `showcase_project` re-runs the page's `find` exactly once (1 to 2), the re-read rows reach the page, and a counter the user clicked before the event still reads 1, so the page was not remounted. - Lit control: an event on `showcase_invoice` does not re-run the read (`find` stays at 1, the rows stay the first answer), while a bare `useDataInvalidation('showcase_invoice')` reader mounted beside the page moves from 0 to 1, so the event demonstrably reached the bus. | run | result | | --- | --- | | base `react-page.tsx` (blob `f70b8f425`, restored from `HEAD` afterwards: blob equal, `git diff HEAD` empty) | `Tests 2 failed (2)`: both tests, each at the page-error assertion, `ReferenceError: useDataInvalidation is not defined` in the page's error panel | | head | `Tests 2 passed (2)` on the implementation's first commit `f64ba6e66`, and again inside the package run below at `af76d224c` | | ablation: the injected hook replaced by a stub that never moves (`ablation-replace`, anchor `useDataInvalidation,` hit once, blob `ab712dfac` to `9625f569b`, restored to the `HEAD` blob, `git diff HEAD` empty) | `Tests 1 failed \| 1 passed (2)`: only the re-read test, `expected 'read 1' to be 'read 2'`; the lit control stays green | The pin imports its subject by relative path, and `@object-ui/react` and `@object-ui/react-runtime` through the root vitest alias to their sources, so no `dist/` sits between a mutation and the run. ## Tests and gates (head `af76d224c`) - `pnpm exec vitest run packages/components/ packages/plugin-view/ --maxWorkers=2` under the verify lock at `af76d224c` (the run printed that sha): `Test Files 379 passed | 1 skipped (380)`, `Tests 3689 passed | 24 skipped (3713)`. - `pnpm exec vitest run scripts/ --maxWorkers=2` under the verify lock at `af76d224c`: the whole `scripts/` test project, so every `scripts/__tests__` gate whose corpus reads a touched file (the changesets, the guide, the package sources) is in it: `Test Files 177 passed | 2 skipped (179)`, `Tests 5332 passed | 2 skipped (5334)`. - `type-check` for `@object-ui/components` and `@object-ui/plugin-view` (echoed `tsc --noEmit && tsc -p tsconfig.test.json`): Done, Done, at `2ca2478ee`, after `turbo run build` over both packages' dependency closures (15 tasks). The one later commit reflows comment and changeset prose only. `tsc -p tsconfig.test.json --listFiles` in `packages/components` names the new pin once, so the test program compiles it. - At `af76d224c`, each exit 0: `check:control-bytes`, `check:new-line-citations` (`0 new citation(s)`), `changeset:check`, `check-changeset-presence` (`3 source file(s) of 2 released package(s) changed, and this change declares 1 changeset(s)`), `check-changeset-overwrite` (reports the plugin-view changeset edit as a correction, its case 2), `check:changeset-claims`, `check:pending-changeset-literals`, `check:test-path-roots`, `check:phantom-deps`, `check:unused-deps`, `check:shell-escape-residue`, `check:doc-fences`, `check:doc-types`, `check:doc-example-ids`, `check:doc-example-readers`, `check:vi-mock-specifiers`, `check:vi-mock-inherit`, `check:vi-mock-override-shape`, `check:comment-mask-corpus`. - ESLint over the three changed source files, `--no-inline-config --format json`: 3 files, 0 errors. The new pin carries 0 warnings. `react-page.tsx` (6) and `ObjectView.tsx` (60) carry the same warnings per rule as their base blobs, read through `--stdin-filename`. This is a narrowed run, not the repo lint: the root ESLint config enables no type-aware linting (no `parserOptions.project`, no `projectService`), so this diff cannot move a verdict on an untouched file. `pnpm lint` is CI's. - `check:doc-snippets` and `check:doc-examples`: NOT MEASURED locally, prerequisite not met (they need the whole covered package set built). The guide's changed block is fenced `jsx`, which `check:doc-snippets` does not compile (its compiled fence set is `ts`, `tsx`, `typescript`). CI runs both. - `check-governed-queue-guard --test` over the 6 changed paths: NOT GOVERNED. ## One case the widened scope can break The scope's names become the parameters of the function the page source is evaluated in. A page that declares its own top-level `const` or `let` named `useDataInvalidation` therefore now fails with a `SyntaxError` (`Identifier 'useDataInvalidation' has already been declared`), shown in the page's error panel; a `function` or `var` declaration of that name only shadows the injected one. Measured once through the real renderer with a throwaway test (panel shown, message as quoted), not kept. Census: no page source in objectui (apps, examples, content, skills) or in objectstack `origin/main` (examples, content, skills, packages) declares the name. The changeset states this case. ## Serial Open PRs, read again just before this PR (12 open, every file list complete under 100 entries): none touches `react-page.tsx`, the `react-page-*` tests, `react-pages.md` or the `10887-*` changesets. One touches `ObjectView.tsx`: PR objectstack-ai#10967 (opened after this claim, route 2's named-view reads). `git merge-tree --write-tree` of this head against its head `600cf8901`: clean, exit 0. Its hunks and this PR's two comment hunks do not overlap. PR objectstack-ai#10777 touches `PageView.tsx`, its tests and its own changesets only. ## Acceptance notes - **(b), not filed.** `ObjectView`'s `fetchDrawsView` names the types it excludes (`grid`, `tree`, `chart`) rather than the types `generateViewSchema` draws, so a view type `generateViewSchema` has no case for (it returns `null`, and `renderContent` falls through to `ObjectGrid`, which queries for itself) still subscribes here and re-reads once per matching event, for rows nothing renders. Pre-existing shape of member 1; carrier: none. - **(c), not filed.** The non-grid fetch sets `loading` at the start of every run, including a bus re-read, and `ObjectCalendar` swaps to its loading placeholder while it is set, the same flash as the calendar's own objectui#10572 reader. A silent re-read would change behaviour; carrier: none. - **objectstack half, beyond objectstack-ai/objectstack#20395's named files.** The spec's generated react-blocks reference (`skills/objectstack-ui/references/react-blocks.md`, written by `packages/spec/scripts/build-react-blocks-contract.ts`) lists the injected scope as `React`, `useAdapter`, `data`, `variables`, `page`. After this lands that list is incomplete. objectstack-ai/objectstack#20395 names `react-pages.mdx`, `rules/pages.md` and the showcase pages, not this generator; it is the natural carrier. --- _Generated by [Claude Code](https://claude.ai/code/session_01DuWo5bdP9SdVebamn99GGk)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
Fixes #10885
Clause-②: no
Member 4 of objectui#10885, under PM claim
5868584175and the seat ruling in5867513786(option A widened bydata, contract review5866387245item 10). Branchclaude/issue-10885-route2-named-view-rest, headbd41f5cf18, base06a96e9480. The gates below ran at600cf89017; fix round 1 changes prose and one test docblock only.Three of the five members land here:
navigation,inlineEditandfieldOrder.labelanddataare ruled B, not relayed on route 2 (seat ruling5869413175).data's provider conflict is objectui#10971. Member 1 (PR objectui#10937) and member 3 (PR objectui#10955) have merged, and member 2 was enumerated, not graded (see Acceptance notes). So this PR closes the card.Fix round 1 (
bd41f5cf18, prose only) applies contract review5869604558's five wording fixes:label/dataare stated with their reasons in both changesets, the README and the docs page;navigationover the create and edit forms, including the New button undersplit/popover(objectui#10975);onRowClickqualifier;The review's two follow-up carriers are objectui#10975 and objectui#10976.
What changed
All code is in
packages/plugin-view/src/ObjectView.tsx, outside both fences.navigationnavigationConfig=currentNamedViewConfig?.navigation ?? schema.navigationactiveViewrung. Its consumers follow it:handleRowClick(theonRowClickroute 2 handsObjectGridand the delegation handsrenderListView),formLayoutand the drawer widthObjectView.namedViewNavigation-10885.test.tsx: route 2 row click, whole-object, delegation row click, drawer width overformLayout, plus 3 controlsinlineEditgridSchema.editable=currentNamedViewConfig?.inlineEditgridSchemaliteral carried noeditablekey at base (0 occurrences in that memo), so there is no node rung.ObjectGridstill ANDs it with the object's inline-edit verdict and theupdategrant (inlineEditable)ObjectView.namedViewInlineEdit-10885.test.tsx(realObjectGrid): an editor opens where the object grants inline edit; no editor whereuserActions.editis false; controlfieldOrderinFieldOrder, applied to both projection slots afterwithoutHiddenFieldsfieldOrderrung. It copiesListView'seffectiveFieldsstep: a stable sort by position infieldOrder, with unnamed or identity-less entries sorted last. A name the projection does not carry orders nothing, and with no projection nothing is added@object-ui/app-shellobjectViewRouteParity.fieldOrder-10885.test.tsx: both routes of the sameObjectViewon the same named view, route 2's projection compared with what the realListViewhands its grid. 4 cases plus 2 controlsRoute 2's comment that said
navigation"is not relayed" now says that it is still not relayed toObjectGrid, and that the handler route 2 passes reads it.Two files outside the claim's surface:
packages/types/src/__tests__/object-view-unmirrored-keys-7779.test.ts: itsREAD_TEXT.navigationpins the exact text of thenavigationConfigline, so it went red at head (1 of 237). The entry now spells the new line, which still readsschema.navigationas the fallback. No census figure moved (see below).packages/app-shell/src/__tests__/objectViewRouteParity.fieldOrder-10885.test.tsx: thefieldOrderparity pin needsListView.@object-ui/plugin-viewdoes not depend on@object-ui/plugin-list, and@object-ui/app-shellis the one package that depends on both.Prose:
packages/plugin-view/README.mdandcontent/docs/plugins/plugin-view.mdx: the member-1 sentence "label,dataandnavigation, also declared on both, are not read off the named view on this path" is replaced. The new text says what route 2 reads now (fieldOrder,inlineEdit,navigation) and what it still does not (label,data). The "Opening a record" section says an active saved view'snavigationreplaces the node's. A grep for the old sentence across the repository hit only these two files and the changeset below..changeset/10885-object-view-route2-named-grid.md(pending): one dated note is appended in the objectui#10533 form. There are 2 insertions and 0 deletions, and the frontmatter md5 is4d3ab4fd127d2f0feaabd7840d888656before and after..changeset/10885-object-view-route2-named-view-rest.md(new):'@object-ui/plugin-view': patch.Fences
Each fence was extracted from its
#regionline through its#endregionline.06a96e9480600cf8901740bc22920811a42b0284f478746ff5a5f6b18d1040bc22920811a42b0284f478746ff5a5f6b18d10a140d0421c41a4401a8b96f6f356b9e8daa9b168a140d0421c41a4401a8b96f6f356b9e8daa9b168Census
object-view-unmirrored-keys-7779andschema-reference-named-list-view-keys-7923:ObjectView.tsx= base blob34f0656469): 237 passed of 237.600cf89017: all passed, in a 5-file run of 315 tests with the other threepackages/typesreaders.The partition is 43 read and 21 unread of 64 on both sides. The pins asserting it are "the renderer reads exactly forty-three names off a named view" and "the census partitions the declaration exactly: 43 read + 21 unread = 64". All three keys are already in the read set.
readSites.labelstays at 2, because nolabelread was added.Red on base, green on head, and per-member ablation
Each leg below started from HEAD. It proved its mutation was on disk with an occurrence count, ran the 3 new pin files (16 tests), then restored with
git checkout HEADand a blob-hash check (git diff HEADempty).ObjectView.tsxreplaced by the base blob (hash equal,inFieldOrder0 hits)navigationConfigback toschema.navigation(named read 0, node-only 1)editableline removed (0 hits)inlineEditfix rowfieldOrderforcedundefined(read 0, ablated 1)ObjectGrid'sinlineEditablewithoutobjectInlineEditableThe 7 base-green rows are the controls. Each base failure is an assertion failure, for example
expected [ 'a', 'b', 'c', 'd' ] to deeply equal [ 'c', 'a', 'b', 'd' ], oronNavigatenot called withr1,view.labelanddata: measured, then ruled Blabel. The order says: stop if relaying paints a second visible title. Measured by relaying it in a scratch copy with the realObjectGrid:LABEL_ALLand the table gains acaptionelement readingLABEL_ALL.ListViewhands its grid nolabel.readSites.labelpin from 2 to 3.labelis already read on route 2, as the tab strip's text.data. The order says: stop ifListViewandObjectGriddisagree on a provider. Measured per provider on the same named view, with nodeobjectName=probe_task:ListView)ObjectGrid)valueitemsitemsobject(other_obj)probe_task, the node (objectui#7477 ruling B: anobjectNamealready present wins)other_obj(record-source ladder:datafirst)apiprobe_taskprobe_taskschemaprobe_taskprobe_taskAt head, route 2 fetches
probe_taskfor all four.Gates (head
600cf89017unless noted)@object-ui/plugin-viewtype-check(tsc --noEmit && tsc -p tsconfig.test.json): exit 0. Both new pins are in the test program (--listFilesOnly).vitest run packages/plugin-view/:Test Files 56 passed (56),Tests 549 passed (549).@object-ui/typestype-check: exit 0. The 5packages/typesObjectView readers: 5 files, 315 tests passed.@object-ui/app-shelltype-check: exit 0, with the new parity pin in the program.Tests 1218 passed (1218). The set was enumerated bygit grepfor files that read the source or import@object-ui/plugin-view: 21 app-shell files, react 9357, plugin-map ×2, plugin-list 4024, 4 script tests, schema-catalog gallery, 3 console tests and cli app-generator.check-changeset-presence: "1 changeset(s) added".check-changeset-overwrite: report-only, case 2,plugin-view: patchbefore and after.check:changeset-claims: 6 pending entries name touched files, and each was read and still holds.check:pending-changeset-literals.check-changeset-no-major.check:new-line-citations:VERDICT … 0 new citation(s).check:control-bytes.check-doc-links: "Links are valid across 17 scan roots".check:spec-symbols.check-doc-component-types,check-prompt-component-keysandcheck-doc-example-ids.check:doc-fences,check-doc-snippet-types,check-doc-example-typesandcheck:doc-example-readers.check:readme-exports(after building cli and plugin-ai): "549 real, 0 wrong-path, 0 fabricated".check:docs-route-closure.check:phantom-deps,check:vi-mock-specifiers,check:vi-mock-inherit,check:vi-mock-override-shapeandcheck:test-path-roots: all exit 0.check:eager-closureaftervite buildofapps/console:eagerGzipBytes3177396,plugin-viewchunk gzip 18798.eagerGzipBytes3177460,plugin-viewchunk gzip 18866.plugin-viewchunk is eager.git merge-tree --write-treeis clean against freshmain95a7c8d38f(treebffa22dd6a) and against PR objectui#10955's headacf69b7835(tree22d3cf4ecc).462c85868d.anytoObjectView.tsx. The repo-widepnpm lintis left to CI.check-governed-queue-guard --testover the 9 paths reports NOT GOVERNED.Acceptance notes
git grep -n "renderListView="over non-test sources finds 3 host sites, two in the console object page and one in the Studio design surface. None of the schemas they pass carrieslistViews: the console page'sobjectViewSchemaliteral has 0listViewshits, andStudioDesignSurface.tsxhas 0. No in-tree host passes both.navigationfix reaches the delegation by construction, through the shared handler. Since no in-tree host passeslistViewsthere, its in-tree reach today is route 2: the StudioViewPreviewand authored nodes.hiddenFields, route 2 keeps a column entry that has no identity andListViewdrops it. ThefieldOrderparity pins use identified columns.labelanddatarelays, and a probe file that imported@object-ui/plugin-listfrom plugin-view. Nothing of them is in the branch.Generated by Claude Code