Repository navigation
feat(plugin-list): a page list-view node is written data / type, and the metadata page reads properties.type as the list kind (objectui#12099) - #12668
Conversation
…the metadata page reads properties.type as the list kind The list-view and view:list registrations publish `data` and `type` in place of `objectName` and `viewType`. ListViewBlock reads a metadata page's kind from `properties.type` (the hoist never copies a bag's `type` onto the node, whose own `type` is the registry key), and no longer reads a page node's own `objectName` / `viewType`. A node that names no record source draws the "no object named" hint naming `data.object`. Claude-Session: https://claude.ai/code/session_01Vk15bz8xHtKGUfXfCm16h4 Co-authored-by: Claude <noreply@anthropic.com>
…e; docs name the page spelling Respells the page-path fixtures that bound a list-view through the retired objectName / viewType to `data` / `properties.type`, teaches the console's binding pins the list-view `data` key and its hint, and documents the page spelling in the plugin-list README and the binding guide. Claude-Session: https://claude.ai/code/session_01Vk15bz8xHtKGUfXfCm16h4 Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vk15bz8xHtKGUfXfCm16h4 Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vk15bz8xHtKGUfXfCm16h4 Co-authored-by: Claude <noreply@anthropic.com>
…arrier pin without any `check:doc-types` reads every `type` literal in a code block as a node type, so the README example keeps the node's own `type` only and names the `properties.type` kind in the prose beside it. Claude-Session: https://claude.ai/code/session_01Vk15bz8xHtKGUfXfCm16h4 Co-authored-by: Claude <noreply@anthropic.com>
✅ 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: Inputs: card objectui#12099 (body and all four comments, the os-dev report ① Derived judgments
Also read, not WRONG: Check-runs on the head (36): 32 success — ② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS PASS is on the head above: the committed tree does what ruling C asks and nothing it forbids. Before ready, the PR body's Generated by Claude Code |
席位核对 — ACCEPT ·
|
✅ 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
|
…12670-list-view-page-face Brings in objectui#12099's renderer half of ruling C (5c0846e) and objectui#12666's page view-data detach (15d8ca6). The two test files both sides edited (imported-defaults-8317, spec-object-refinements-7715) auto-merged, and each keeps both intents. Claude-Session: https://claude.ai/code/session_01Vk15bz8xHtKGUfXfCm16h4 Co-authored-by: Claude <noreply@anthropic.com>
Fixes #12099
Clause-②: yes (narrowing)
Ruling: objectstack-ai/objectstack#22444, comment
6100463199(ruling C). Claim: comment6213954533on objectui#12099. Written by the dispatched os-dev of thedomain:uiseat 3, sessionsession_01Vk15bz8xHtKGUfXfCm16h4.What changes
packages/plugin-list/src/index.tsx).list-viewand itsview:listalias (one renderer, so one spelling) publishdata(object: the spec'sViewData,{ provider: 'object', object }names the object) andtype(enum: the nine kinds a list view draws) in place ofobjectNameandviewType. Neither is required: adataSourcebinding can name the object.list-view'sdefaultPropsare{ data: { provider: 'object', object: '' }, columns: [], filter: [], sort: [], options: {} }. There is notypedefault: an absent kind is already the grid (the spec's list viewtypedefaults togrid), absence lets a bound saved view's kind apply, and atypethere would read as the registry key to anything that spreads the defaults onto a node.ListViewBlock).SchemaRenderer's hoist copies a node'spropertiesonto the node, never copies the bag'stype(the node's owntypeis the registry key), and keeps the bag on the node. So thelist-viewrenderer readsproperties.typeitself and hands it toListViewasviewType, before the element-data-source gate, so a kind the node states outranks a bound saved view's kind. A value that names no kind a list view draws is dropped, and the list is a grid.HOIST_PROTECTED_KEYSis unchanged (hypothesis 2 below).ListViewBlockremoves a page node's ownobjectNameandviewType, flat or hoisted from the bag, before the gate andListViewsee the node. Each logs one console warning per process naming what to write instead. A node that names its object only asobjectNametherefore names none, and draws the "no object named" hint, which now namesdata.object.ListViewBlockdraws that hint after the binding is applied; the gate's ownrequiresObjecthint names the gate's object key, which isobjectName.datapasses through as authored.normalizeListViewSchema's existingobject-provider fold (objectui#7477) lands it onListView'sobjectName. A bounddataSource.objectstill lands onobjectNamethrough the gate and outranksdata.object.objectNameandviewTyperemain the hand-off keys between the gate and theListViewcomponent; neither is read off the page node.dataandtype", and the page-binding section now namesdata.content/docs/guide/data-source.md:list-view's object key isdataand its hint namesdata.object..changeset/12099-list-view-page-spelling.md,@object-ui/plugin-listminor, with the breaking part for page nodes in the old spelling spelled out.The PM's mechanism hypotheses, measured
8614705: the registration's inputs carried the sharedobjectNameinput and aviewTypeenum, itsdefaultPropsobjectName: ''andviewType: 'grid', andSchemaRendererdeclaredHOIST_PROTECTED_KEYS = new Set(['type', 'id']). A scratch probe (deleted) through the realSchemaRendererand the real registration, with view spies:{ type: 'list-view', properties: { data: { provider: 'object', object: X }, type: 'kanban', kanban } }drew a grid over X (thedatahalf already bound; only the kind was lost), and the flat old spelling drew a kanban.propertieson the node after copying it, so the renderer can readproperties.typeitself. A global change would reach the two specComponentPropsMaprows that declare atypemember,page:tabs(the tab style) andelement:metadata_viewer(2 of 59 rows, read from the installed@objectstack/spec18.0.0-next.1). Chosen: the narrow read inListViewBlock.data(the fold above) and fromdataSource.object(the gate). What moved: the node'sobjectNameis not read, and the hint judgesdata(anobjectprovider counts only with a non-emptyobject; rows and any other provider count as a source). The gate still lands a binding onobjectName, as the internal hand-off: landing it "intodata" would either overwrite an authored rows orvalue-providerdataor need a new gate option in@object-ui/react, outside this claim.objectui validaterecord-source rule did not move: it lives in@object-ui/types(thelist-viewarm'srequireRecordSourcecall). See the stop below.ListViewBlockis aforwardRefoverListViewProps, whoseschemais@object-ui/types'ListViewSchema, the zod-derived face thatobjectui validateandStrictAnyComponentSchemajudge alist-viewnode with (typeis the'list-view'discriminator literal;objectNameandviewTypeare its members; it has nopropertiesmember). Measured on that face at8614705: the ruled page node is refused (strict:unrecognized_keysproperties, plusRECORD_SOURCE_REQUIREDatobjectName; tolerant: the record-source issue), a flatdata-only node is refused the same way, and the flat old spelling is accepted. Separating the two needs a@object-ui/typeschange, a contract face, so it is not in this PR (open question below).Census: nodes written with
objectName/viewType(hypothesis 5)Instrument: a node-literal scan over
git ls-files(alist-vieworview:listtype literal in TS or JSON, or the html tag) withobjectName/viewTypewithin the next 8 lines. Positive control: the firstListViewexample inpackages/plugin-list/README.md(hit). The ruling's count of about 7 files and 45 mentions is its own reading. Non-test result at8614705, 5 files and 12 sites:packages/plugin-list/README.md, 8 sites: each one an example of theListViewReact component'sschemaprop, not a page node.packages/app-shell/src/views/InterfaceListPage.tsxandpackages/app-shell/src/views/ObjectDataPage.tsx: each renders theListViewcomponent directly with the schema it builds, not throughSchemaRendererand the registration.packages/plugin-view/src/ObjectView.tsx: the schema handed to a host'srenderListView; the console's host (app-shellObjectView) renders theListViewcomponent with it.packages/plugin-list/src/ListView.tsx: one doc comment.So no objectui source, README or guide writes
objectName/viewTypeon a node that reaches thelist-viewregistration, and no runtime-built node in this repository reaches it. The four component-prop sites stay as they are: on that typetypeis the discriminator, so the kind cannot be spelledtypethere without the@object-ui/typeschange. The page-path test fixtures that bound a list through the old spelling were respelled (Tests).Stopped, not shipped: the
@object-ui/typesfaceobjectui validate(both faces) and this repository's owncheck:doc-jsongate still judge alist-viewnode in the spelling before this PR: they accept a flatobjectName/viewTypenode that the renderer no longer reads, and refuse the ruled page spelling, telling the author to declareobjectName. The README's new page example therefore carries adoc-json: fragmentdeclaration naming this; the gate re-judges it on every run, so it turns red (stale) the day the face accepts the page spelling. The decision request is in the os-dev report on the card.Contract evidence (Clause-②)
Clause-②:line: corrected by the dispatching seat fromyes (widening, with the old spelling retired)toyes (narrowing), per the contract review of record6216255338(its ②). The diff widens and narrows; the closed pair takes one arm, and the narrowing arm is the one that decides the review owed (CONTRACT_REVIEW_TIER). Nothing in the tree moved.@object-ui/plugin-listbuiltdist, measured: built once with the8614705versions ofsrc/index.tsxandsrc/ListViewBlock.tsxand once at this head. All 10dist/*.d.tsfiles are byte-identical. The runtime contract moved indist/index.js:list-view's inputsobjectNameandviewTypebecamedataandtype(the same onview:list), and itsdefaultProps{ objectName: '', viewType: 'grid', … }became{ data: { provider: 'object', object: '' }, … }.@object-ui/react: no file underpackages/reactorpackages/typeschanged (an emptygit diff 8614705over both), so neither built output moves.list-viewreadsproperties.typeas its kind, and the published inputs gaindataandtype. Narrows: a page node'sobjectNameandviewTypeare no longer read, and the published manifest no longer lists them, so an html-tierobjectNameorviewTypeattribute now drawsunknown-prop.Cross-repo consumers (named, not changed here)
HotCRM: its home page builds embedded list views with
properties: { objectName, viewType: 'grid', … }(hotcrmsrc/sales/pages/home.page.ts, per triage6101018189). Under this PR alone those lists draw the "no object named" hint.objectstack:
sdui.manifest.jsonlists renderer inputs; the console build's manifest now listsdataandtypeforlist-viewandview:list.For the cloud seat, verbatim from triage
6213876820:(There
#22444is spec(ui):ComponentPropsMaphas nolist-viewrow, so a page-embedded list view'spropertiesare judged by nothing:kanban.groupFieldandoptions.gridpass on a page while every list-view door refuses them objectstack#22444: the ADR-0087 conversion of stored page nodes,objectNametodataandviewTypetotype, that lands with the console pin move.)Tests
packages/plugin-list/src/__tests__/ListViewBlock.pageCarrierSpelling-12099.test.tsx, through the realSchemaRendererand registration: the card's kanban throughproperties.type(bound to the objectproperties.datanames, queried, and theobject-kanbannode is the oneListViewbuilds, so the node dispatched aslist-view); a grid whenproperties.typeis absent or names no kind; the same onview:list; ⛔ no read ofobjectName/viewTypein the bag or flat (hint namingdata.object, no query); aviewTypebeside a workingdatachanges nothing; one warning per retired key per process;properties.typebeside adataSourcebinding, outranking a bound saved view's kind, with the view's kind applying when absent; the registrations' inputs anddefaultProps.ListView.sharedGate(the authored-kind case now authorsproperties.type),ListView.elementDataSource,ListView.invalidationRefetch-10572,ObjectGallery.filterContextTokens-10666,htmlTierColumnSpellings-6598(form 8a now expects the hint to namedata.object),htmlTierListViewDefaultColumns-6598; consolefilterContextTokensSweep-10666,htmlTierDataSourceInputDeclaration-6678,objectname-neither-hint-11605(list rows name their object indata, and a new row pins that the retiredobjectNamedraws the hint),objectname-binding-required-11605(the object key read off the binding doc, plus a row pinningunknown-propfor the retired attributes),public-block-binding-reach(list-viewleft the population of blocks that declareobjectName, with the reason).9e4c5ee,pnpm exec vitest run --maxWorkers=2over the wholepackages/plugin-list/plus 71 named files elsewhere that render alist-viewthrough the registry or read its published inputs (console manifest and html-tier pins, app-shell list surfaces, plugin-view, react, components, sdui-parser, types):Test Files 212 passed (212)/Tests 2904 passed (2904)(lock verdictcommand-exit 0).pnpm --filter @object-ui/plugin-list type-check(both projects; the test project includes the new pin) exits 0 afterpnpm --filter '@object-ui/plugin-list^...' build.ablation-replace.mjswith the restore proven by blob hash and an emptygit diff HEAD, direction predicted before running:properties.typehand-off: predicted and observed 5 red (the kanban case,view:list, both binding cases, the sharedGate authored-kind case), 30 green.objectName-only hint cases, theviewType-beside-datacase, the warning case), 14 green.Gates
Local subset, each exit code captured before any pipe, at
9e4c5ee:check:control-bytes,check:new-line-citations(0 new citation(s)),check:i18n-keys(the hint's inline default equals theenpack),check:doc-types,check:doc-json(the README page example sits in thedeclaredbucket, re-judged as refused by the face),check:doc-fences,check:element-data-source-declaration,check:component-surface-parity,check:handler-key-reads,check:changeset-claims,check:pending-changeset-literals,check:test-path-roots,check:prompt-keys,check:registry-bare-names,check:spec-symbols,scripts/check-changeset-presence.mjs,scripts/check-changeset-no-major.mjs;eslint --no-inline-configover the 14 changed TS files: 0 errors (the warnings are the existinganypatterns of the respelled files; the new pin carries none).scripts/check-governed-queue-guard.mjs --testover the 17 changed paths: NOT GOVERNED.check:doc-snippets(exit 2, precondition: 34 packages unbuilt; no ts fence changed),check:sdui-registration-pins(exit 2, needs a console build),check:readme-exports(exit 1 on 382 self-imports it could not judge, all in packages outside this build closure; plugin-list's README drew none, and the judged set has0 wrong-path, 0 fabricated), the consoletype-check(its closure is the whole workspace; only test literals changed there),pnpm lintand the full test farm.Acceptance notes
typeattribute by design (the tag is the component), so an html-tier list-view tag cannot state a kind; a bound saved view's kind still applies. Thetypeinput is published and reachable on metadata pages and on the react tier (whosetypeattribute is parked asspecType, unchanged).check:doc-typesreadsproperties.typeas a node type: everytypeliteral in a code block is judged against the registry, so a page example with"type": "kanban"inpropertiesis flagged. The README states the kind in prose beside the example instead. Noted, not filed.ListViewwithtype="kanban"and adataSourcenaming a saved view parks the kind asspecType, whichnormalizeListViewSchemareads only whenviewTypeis absent, and the gate writes the view's kind toviewTypefirst; so there the saved view's kind would win over the author's. Not changed here (the react tier's carrier), not filed (not reproduced).Generated by Claude Code