feat(spec)!: retire the ListView objectName / viewType react-tier aliases — data / type are the only spellings (#14791) - #16777
Conversation
…ases — data / type are the only spellings Maintainer ruling on #14791 (2026-09-07, director seat summon #17, decision batch #1, option B): the two overlay props #11284 had deprecated are removed from the ListView block with no deprecation window, now that the consumer fold ships in the pinned console (objectui normalizeListViewSchema at a472b071). - react-blocks.ts: objectName / viewType gone; `data` restated as the required binding (ledgered in REACT_OVERLAY_SHADOWS); REACT_RETIRED_OVERLAY_PROPS is the tombstone ledger; the record:related_list alternative writes the canonical spelling. - lint: boundObjectName reads data.provider === 'object' for ListView (the canonical read step 1 deferred); a retired spelling is a react-prop-retired error carrying the prescription; the step-1 unfolded-deprecation scaffolding is deleted. - showcase pages, the published objectstack-ui skill, the react-pages and validating-metadata guides and one recognizer fixture write the canonical spelling. - ADR-0087: semantic entry ui-react-list-view-binding-aliases-retired under protocol major 18; changeset minor with the BREAKING banner. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016N6xmWt5hYm94ffVEwGH8x
…t-origins and the migration registry; pay the pages.md token ratchet; keep the tracker id out of the lint message Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016N6xmWt5hYm94ffVEwGH8x
…tire-listview-objectname
📓 Docs Drift CheckThis PR changes 2 package(s): 6 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 5 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 130 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin b2299e746eebb743a502321d15746fcced229b6c && git checkout b2299e746eebb743a502321d15746fcced229b6c
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 68f8f77205a32f1a0355b9a3cccf006538cfbe9f 5b010e033266bf46d8bf4a083680963eb413fbb5 && git checkout -B drift-repro 68f8f77205a32f1a0355b9a3cccf006538cfbe9f && git merge --no-ff 5b010e033266bf46d8bf4a083680963eb413fbb5
node scripts/docs-audit/affected-docs.mjs --json 68f8f77205a32f1a0355b9a3cccf006538cfbe9f
|
…inputs in the declaration-parity baseline, with their discharge condition The gate's own --update path (MANIFEST=sdui.manifest.json check:react-declaration-parity --baseline react-declaration-parity.baseline.json --update), then the hand-maintained _acceptedReasons block re-added as the baseline's _note prescribes, with two new entries that state the expiry: accepted only until objectui#8510 removes the two designer inputs from objectui's list-view registration. This moves a ratchet as the mechanical consequence of the #14791 ruling (option B, no deprecation window); declaring the props back in spec or on the overlay would undo that ruling and is not an exit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016N6xmWt5hYm94ffVEwGH8x
Contract review — REWORK, one blocking line. Head
|
…ing fixture
The `parseable` array in `validate-react-page-props.test.ts` is the
FALSE-POSITIVE CONTROL for the syntax gate: every entry asserts only
`not.toContain(REACT_PAGE_SOURCE_UNPARSEABLE)`, so it grades the PARSE and
nothing else. One entry carries an `ObjectForm` and a `ListView` in a single
fragment. Retiring the `ListView` binding aliases re-spelled BOTH halves to
`data={{ provider: "object", object: "a" }}`, but only the `ListView` half is
in that retirement's scope: `ObjectForm` binds by its own props and carries the
shared `OBJECT_NAME` overlay (`packages/spec/src/ui/react-blocks.ts`, the
`REACT_BLOCKS` entry for `ObjectForm`), which is `objectName`, required. It has
no `data` prop at all — neither in its `interactions` nor in its `dataProps`.
The fixture therefore spelled a prop the contract does not carry. Because the
array grades parseability only, both spellings parse and CI stayed green: no
gate in the repo could see it.
Restore the `ObjectForm` half to `objectName="a"` and keep the `ListView` half
canonical, which is what the entry was — a genuine MIXED-SPELLING fragment, and
a stronger parse fixture than either uniform spelling.
Measured over whole file text (never line-oriented, so a hard-wrapped
occurrence cannot hide), across the full diff versus the merge base: the
`objectName=` prop sites attributed to `ObjectForm` are 16 -> 16 and to
`ObjectChart` 36 -> 36 — both unchanged — and `ListView` is the only tag that
gains the canonical `data` spelling.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016N6xmWt5hYm94ffVEwGH8x
ACCEPT — at-tier PASS on head
|
Contract review (
|
Fixes #14791
Executes the director-seat ruling (summon #17, decision batch #1, 2026-09-07, comment 5572008848, maintainer verbatim 「同意」), option B: the ListView react block's
objectNameandviewTypeprops are REMOVED — not un-deprecated — and the only spelling isdata={{ provider: 'object', object }}/type; the lint's canonical-first branch stays; the in-repo author sites and the publishedskills/**passages move in the same PR; option A is refused.Clause-②: yes(a published contract's accept set narrows) ⇒ this PR carriesneeds:contract-review. The diff touchesskills/**⇒ GOVERNED: draft only, a human merges — never ready, never queued, never auto-merged by a seat.What changed
packages/spec/src/ui/react-blocks.ts): the two overlay rows and theirdeprecatedblocks are gone; the ListView summary names the canonical keys as the only spellings;REACT_RECORD_BLOCK_ALTERNATIVES['record:related_list'](the:167string the contract publishes) prescribesdata={{ provider: 'object', object: 'CHILD' }}instead of the retired alias. Two additions are described under "Beyond the ruling's literal words" below.packages/lint/src/validate-react-page-props.ts):boundObjectName(tag, values)readsdata.provider === 'object'→data.objectfor ListView (the canonical read step 1 deferred to "step 3, once the consumer fold has landed" — it has, objectuinormalizeListViewSchemaat console pina472b071); every other block still binds byobjectName. The step-1 scaffolding its own comments said step 3 deletes —UNFOLDED_DEPRECATIONS,replacementUnread,objectProviderBindsNothing, the "renders an empty list" message variants — is deleted. The generic replaced-by satisfaction and the generic deprecation warning stay as mechanisms.examples/app-showcasecrm-workbench(1 site),renewals-pipeline(2 sites, one carrying the tree's onlyviewType),task-desk(1 site) → canonical spelling. Published skillskills/objectstack-ui/rules/pages.md:180and:218→ canonical spelling. Hand-written guidescontent/docs/ui/react-pages.mdx(6 passages) andcontent/docs/deployment/validating-metadata.mdx(3 passages, caret annotations re-aligned) → canonical spelling, because this change would otherwise turn them into copy-and-fail examples. One recognizer self-test fixture inscripts/check-react-page-adapter-contract.mjsre-spelled (the assertion is about fence selection, the spelling was incidental).skills/objectstack-ui/references/react-blocks.md(two rows out,datarow now a required binding),packages/spec/api-surface/ui.json,packages/spec/export-origins/ui.json,packages/spec/src/migrations/registry.ts— all viacheck:generated --fix, only the proved-stale set;check:generatedreports 15/15 up to date at the final head..changeset/react-list-view-binding-aliases-retired.md—@objectstack/specminor,@objectstack/lintminor,@objectstack/example-showcasepatch; BREAKING banner; FROM → TO table and the one-line fix; dispositionregistered ui-react-list-view-binding-aliases-retired(asemanticentry under protocol major 18: react page source is a JSX stringobjectstack migrate metacannot rewrite by key, so the prescription is by hand). D7/D8 do not apply — the diff is inpackages/spec/srcand the body carries a prescription.spec-changes.jsonand the upgrade guide are unchanged on purpose: major-18 entries are not projected yet (control: an existing 18 entry,ui-record-blocks-unknown-keys-refused, is absent from both too).Premise check — the census the ruling ordered, by KEY IDENTITY
objectNameis not one key. The discriminator used at every site: only a prop written on the JSX tagListViewinkind:'react'page source is the react-block prop this ruling retires. A metadata-tierproperties.objectNameon anobject-metric/object-grid/object-formnode (the host stamps it), a navigation item'sobjectName, an action target, a flow node config, a hook parameter, and theobjectNameonObjectForm/ObjectChart(which keep the sharedOBJECT_NAMEoverlay, pinned by test) are all different keys and are untouched — re-measured at5b010e033over WHOLE FILE TEXT rather than line by line, so a hard-wrapped occurrence cannot hide: across this whole diff versus the merge base, theobjectName=prop sites attributed toObjectFormare 16 → 16 and toObjectChart36 → 36, andListViewis the only tag that gains the canonicaldata={{ provider, object }}spelling. That claim was FALSE at4ee841b4: oneObjectFormin the parse gate'sparseablearray had been re-spelled todataalong with theListViewbeside it in the same fragment — a propObjectFormdoes not carry, since itsREACT_BLOCKSentry declares the sharedOBJECT_NAMEoverlay and nodatain eitherinteractionsordataProps. That array assertsnot.toContain(REACT_PAGE_SOURCE_UNPARSEABLE)and nothing else, so both spellings parse and no gate in the repo could see it.5b010e033restores that half toobjectName="a"and keeps theListViewhalf canonical, which is what the entry was: a genuine mixed-spelling fixture.origin/main5e53d73)skills/**(11 files)pages.md:180,:218) + the generated rowsnavigation.md), action targets ×4, flow configs ×7, hook param, i18n record,ObjectChartindashboards.mdand an eval, nav-item row inSKILL.md,object-metric/ObjectForminpages.mdexamples/app-showcasepages (8 files)properties.objectName,ObjectForm×5,ObjectChart, commentsexamples/app-crmcrm-workbenchshowcase pagecontent/docshand-writtenpackages/spec/scriptsreact-blocks.ts:167; one recognizer fixture61e649absdui-workbench-preview.tsx:111) — a FORK, reported as objectui#8510plugin-list/README.md:200is the component-schema typed47e37acobjectNamein 114 files, so the grep is live6029352Two PM mechanism assumptions measured the other way: (2)
skills/objectstack-ui/rules/pages.mdhas FIVEobjectNamehits, not zero, and the card's:180/:218citations are exactly the two in-scope ones; (4) the "showcase ×3, CRM" of the ruling is three showcase files carrying four sites —examples/app-crmhas none. The pinned objectui imports nothing from the react-blocks overlay (git grep REACT_BLOCKSata472b071: a docs page and a changeset only), so the Console Pin Gate is not threatened by the removal.Beyond the ruling's literal words — two design points to review
datais restated on the ListView overlay as its REQUIRED binding, ledgered inREACT_OVERLAY_SHADOWS(the mechanismnavigationalready uses). Reason:ListViewShapeSchema.datais optional because a metadata page's host stamps the object; on a react page nothing does, andobjectNamewas the contract's only required binding — removing it alone would let a bare ListView validate green and render empty, the exact class this card closes. The generated contract now marksdata✓ and the lint's missing-required check fires with the contract's own description as the hint. One-line revert if refused: drop the overlay entry and the ledger row.REACT_RETIRED_OVERLAY_PROPSis a new export of@objectstack/spec/ui, and the lint raisesreact-prop-retired(error) from it. Reason: the lint's unknown-prop path is a typo guess within edit distance 2, so a leftoverobjectNamewould otherwise be SILENT — and objectui's renderer still reads that key directly, so the page would keep shipping on it. The tombstone is the react-tier twin ofretiredKey(); the finding carries the FROM → TO. When the retired spelling of a binding is on the element, the missing-required finding defers to it (one finding, one fix).Serial constraint — declared, not silently taken
Files inside the predecessor seat's logical reservation for the #15442 / #15449 re-dispatch (both
pm:blockedon #16626, no dev, no PR):examples/app-showcase/src/ui/pages/crm-workbench.page.ts,renewals-pipeline.page.ts,task-desk.page.ts(the four ListView sites only) andpackages/lint/src/validate-react-page-props.test.ts(37 ListView fixtures re-spelled; the#11284 → #14791describe block rewritten). The re-dispatch should rebase over these knowingly. Fence honoured:packages/spec/src/data/analytics.zod.tsandruntime/src/domains/analytics.tsuntouched. Files outside the claim's declared surface, each required by a clause of the ruling itself:packages/lint/**(the lint clause),content/docs/**(copy-and-fail examples this change would create),packages/spec/src/migrations/**+.changeset/**(the ADR-0087 clause), the generated artifacts, one script fixture.Verification (final head
50ec9f28a= my work merged withorigin/maincf33adbd8; every exit captured before any pipe)scripts/pm/os-verify-lock.sh(OS_VERIFY_LOCK_SLOT=issue-14791): spec (VERDICT command-exit 0, 252s), spec again after the registry regeneration (0, 183s, waited 404s), formula+lint (0, 223s), theclient-reactclosure (0, 425s, waited 185s), spec+formula+lint on the merged tree (0, 17s cache).packages/specreact-blocks.test.ts+migrations.test.ts→ Test Files 2 passed, Tests 129 passed;packages/lintvalidate-react-page-props.test.ts→ 1 passed, 162 passed. Typecheck spec and lint → exit 0 including bothcheck:test-typecheckledgers.validateReactPagePropsfrom the built lint over the three showcase page sources → 0 findings; control on the same call, retired spelling → 2×react-prop-retired.pnpm --filter @objectstack/example-showcase typecheck / validate / testare NOT MEASURED here: prerequisite failures (unbuilt connector/plugin closure; the CLI'svalidatecommand absent without its build; a SQLite env error inhook-body-persisted-writes); thereact-page-adapter-query-contracttest is not among the 12 resolve-entry failures. CI runs the closure.dispatch-gates.mjs --commands --repo objectstack-ai/objectstackat50ec9f28a(127 families, no stale-tree warning);--ranreconcile: 126 run, 1 UNRUN. Exit 0 on 124; NOT MEASURED:check:dual-build-cjs-loadsandcheck:type-check-debt(exit 3, PREREQUISITE NOT MET — every package's dist);check:react-declaration-parity(EXTERNAL_INPUT_REQUIRED — predicted delta: ListViewregistryOnlygainsobjectName/viewTypeon the nextsdui:manifest, owned with objectui#8510);check:pm-dispatch-gatesran detached per its own header — verdict line1552 cases pass, exit code not captured by design. After the mergeorigin/mainmoved 4 more commits (941232040:lint.yml,package.json,scripts/ci/select-gate-families.*— CI plumbing) which the derivation would add; those belong to the PR's CI, not re-chased here.skills/**line budget (PM-set net +0, cap +10): net −2 — tree 13,581 → 13,579;pages.md448 → 448; generatedreact-blocks.md117 → 115. Token ratchet (check-skills-token-ratchet, ceil(bytes/4)): the canonical spelling putpages.mdat 5506 against a 5501 ceiling; paid with 28 bytes of genuine deletion in the same passage (the clause "however you bind them", the placeholderlookup field→lookup) → 5499/5501. No ceiling was raised.const ret = block.retired.get(u)→ undefined, marker line), mutation proven on disk (anchor 1 → 0, marker 0 → 1, blobcdbdb058≠ HEAD blobc2955e5d), no build leg (the suite imports the rule by relative path). Predicted RED on the retired-spelling pins only; observed: 4 failed / 158 passed, exactly the fourreact-prop-retiredpins. Restore bygit checkout HEAD -- ABSOLUTE_PATHunder an EXIT/INT/TERM trap, proven by state:git diff HEADempty, porcelain empty, blob back toc2955e5d.pnpm lintwas not run.eslint --no-inline-config --format jsonover the changed source files at50ec9f28a: 19 files (count read from the JSON), 0 errors, 0 warnings; the narrowing cannot move any untouched file's verdict becauseeslint.config.mjs:327-328states the config never enables type-aware linting for any file.验收备注
react-blocks.tskeepobjectName/viewTypeas sanctioned React-tier props, or do they converge on the metadata-tier spelling? — the carrier objectui#2890's last phase has never had #11284)" with the tracker id inside the runtime string — pre-existing, baselined bycheck:doc-authoring; 承接者:无.datarow's type column is free text (the generator renders the schema'sdataasobject); the restatement is meaning-preserving by ledger, not by a type check — the shadow test pins the ledger, not the wording; 承接者: this PR's contract review.Blocked-by: #15422; the ruling says the seat drops it at claim — the PM's edit, not made here.list-viewdesigner inputs that turn registry-only under the parity ratchet.维护者速读(草稿)
改了什么。 ListView 这个 react 页面组件,原来有两种绑定对象的写法:老写法
objectName="account"和新写法data={{ provider: 'object', object: 'account' }}(视图类型同理:viewType与type)。本 PR 把老写法从契约里删掉,只留新写法;校验器遇到老写法会报错并直接给出改法;我们自己仓里的 4 个示例页、已发布 skill 的 2 处、文档里的 9 处全部改成新写法。为什么改。 你 9 月 7 日的裁决(选 B,不设过渡窗口)。前端那一半(把新写法折叠到组件真正读的键上)已随 console pin
a472b071落地,两种写法今天都能渲染;老写法的仓外用户是 0,新写法与元数据层、与其它六个组件一致。风险与代价(含回滚)。 破坏性契约变更,changeset 标 BREAKING,ADR-0087 登记为手工迁移(react 页面源码是字符串,迁移工具改不了)。仓外若有人写了老写法,校验期会被响亮拒绝(不是静默失败)。objectui 自己的文档和一个预览页还写着老写法,已立 objectui#8510 跟进。回滚:revert 本 PR 即可恢复两种写法,前端不依赖本次改动。两处超出裁决字面的设计点(
data设为必填绑定;新增退休台账导出)各可一行撤回,见上文。席位意见。 (留空,席位定稿成评论)
你要做的。 这是受管面(
skills/**)PR:请你人工确认并合并;确认前先看「Beyond the ruling's literal words」那两点是否同意。⚠ THIS PUSH MOVES A RATCHET —
check:react-declaration-paritybaseline accept (head4ee841b4e)CI's
Type Check · consumer gatesreported, on head50ec9f28a, exactly the delta this PR had declared UNRUN and predicted:ListView: new registry-only input(s) not in baseline: objectName, viewType. objectui'splugin-listregistration still declaresobjectName(required) andviewTypeas designerinputs; with the overlay rows retired by the ruling, the spec no longer declares them, so the parity ratchet sees two new registry-only inputs.What moved.
packages/spec/react-declaration-parity.baseline.json— theListViewrow'sregistryOnlygoes from[]to[objectName, viewType], written by the gate's own accept path (MANIFEST=sdui.manifest.json … --baseline react-declaration-parity.baseline.json --update), never by hand-editing the row; then--strictre-run:✓ no new DECLARATION divergence vs accepted baseline. The gate will no longer report this pair. No other block row changed.Why this path. The gate's two other exits — declare the props in the spec schema, or put them back on the block overlay — would UNDO the maintainer ruling (#14791, 2026-09-07, option B, no deprecation window). Waiting for objectui#8510 is circular: it is
Blocked-bythis card. So the accept is the mechanical consequence of the ruling, recorded through the gate's sanctioned path — not a gate weakening — and it reaches the maintainer before it lands because this PR is GOVERNED and ends in a human merge.Discharge condition — recorded in the baseline itself. The file's hand-maintained
_acceptedReasonsblock (which--updatedrops and its own_notesays to re-add, "an entry with no reason here is an accept nobody ruled") now carriesListView.objectNameandListView.viewType, each marked TEMPORARY with the expiry: accepted only until objectui#8510 removes the two inputs from objectui's list-view registration (the metadata host stampsobjectName;normalizeListViewSchemasupplies it fromdataat pina472b071). When that lands and the pin moves past it, delete both reason entries and the two inputs from theListView.registryOnlyrow. The PM authorised this accept explicitly as the ruling's consequence; the diff of this push is the baseline file and this disclosure, nothing else.Generated by Claude Code