Skip to content

feat(spec,lint): ListView react-tier vocabulary converges on the metadata-tier spelling, deprecate-first - #11695

Merged
os-warren merged 2 commits into
mainfrom
claude/issue-11284-react-tier-vocab-converge
Aug 24, 2026
Merged

feat(spec,lint): ListView react-tier vocabulary converges on the metadata-tier spelling, deprecate-first#11695
os-warren merged 2 commits into
mainfrom
claude/issue-11284-react-tier-vocab-converge

Conversation

@os-warren

Copy link
Copy Markdown
Collaborator

Fixes#11284

Implements the maintainer ruling recorded on-card (2026-08-23, comment 5386670344, verbatim 「10950 不考虑存量,其他接受你的建议」): converge — the react tier adopts the metadata-tier spelling, via deprecate-first retirement discipline. This PR is the deprecate-first step ONLY; removal of the aliases is a later card. Unblocks objectui#2890 (its A6 family was Blocked-by: this card; #2890 remains open — no closing keyword intended for it).

What converges (ListView only)

  • Canonical props are the spec ListView schema's own keys, surfaced through the curated dataProps: the object binding is the metadata-tier data source (objectui#2890 A6 verbatim: objectName maps to the data source with provider object and its object key), and the visualization kind is type (riding the generator's explicit-allow for type — the ObjectChart 图表绑定方言统一:ObjectChart 运行时兑现 ChartConfig 后,把 react 契约与 lint 切回 spec 形状(#3701 后续) #3729 precedent; the react-page wrapper parks an author type beside the SDUI discriminator as specType, objectui#2880).
  • objectName / viewType stay published as deprecated aliases for the whole window: [DEPRECATED → …] markers in the generated contract (the established textual convention — FormViewSchema groups / drawerWidth), plus a machine-readable deprecated: { replacedBy, note } on the overlay entry, passed through to the contract JSON.
  • Lint (validate-react-page-props): new warning rule react-prop-deprecated (severity warning, never error — the approval-approver-type-deprecated precedent) on every use of a deprecated spelling; the required binding is satisfied by either spelling (canonical accepted without the alias); field-name props (columns, searchableFields, filter positions, …) resolve against the object bound by whichever spelling is present, canonical winning when both are — mirroring objectui's normalizeListViewSchema fold direction. Warnings never block: publish gate refuses on error only ([runtime/metadata] 作者时规则只存在于 CLI:Studio/REST/MCP 的运行时授权面是第四扇门,26 条规则一条不跑——#4409 修完后最大的敞口 #4463 P1), os lint exits 1 on errors only (read from packages/cli/src/commands/lint.ts).

What deliberately does NOT converge in this step

  • ObjectForm objectName — objectui#2890 Scope B states its spec counterpart (FormViewSchema) is "not 1:1" and wants a field-level audit before any swap; defineForm stamps data with provider schema on every metadata form, and the record-form provider values are documented as possibly unread.
  • ObjectChart objectName — no metadata-tier spelling exists to adopt: chart.zod.ts's own retired-key guidance declares the objectName PROP the sanctioned react binding, and the metadata tier binds charts through a dashboard dataset (ADR-0021), a semantically different mechanism.
  • Both are pinned by a test (ObjectForm and ObjectChart objectName are NOT converged by this step) and raised as open questions in the dev report for a follow-up ruling.
  • conditionalFormatting / exportOptions untouched — the filer's fence, upheld in the ruling.

Deprecation-window delivery note (sequencing, for the reviewer)

At the current console pin, plugin-list's ListView reads schema.objectName and honors schema.data only for the value/api providers; ObjectGrid already resolves data provider object canonical-first. The canonical spellings on the react ListView are therefore ahead of the pinned renderer until objectui#2890 A6 lands (the work this PR unblocks). The in-repo example pages deliberately stay on objectName (still accepted, warns advisory) for that reason. This is the ruled deprecate-first ordering: spec declares canonical + loud deprecation first, the consumer migrates inside the window, removal comes after.

Mechanism findings (PM assumptions, tested)

  • The card's react-conformance.baseline.json does not exist under that name; the ratchet is packages/spec/react-declaration-parity.baseline.json + check-react-blocks-declaration-parity.ts. It needs no movement in this PR, verified by mechanism: the gate excludes overlay props from both the spec-only and registry-only directions, and the deprecated aliases remain overlay entries; specProps() excludes type by name; data was already a schema prop. The gate is EXTERNAL_INPUT_REQUIRED (objectui sdui manifest, browser dump) and runs on-demand at pin bumps by maintainer ruling 2026-08-07 — not in this PR's CI.
  • objectui checkout carries no test importing REACT_BLOCKS; its nearest parity surfaces are apps/console/src/__tests__/public-block-binding-reach.test.tsx (registry-config-driven) and packages/types/src/__tests__/list-view-spec-parity.test.ts (types-level) — neither reads the spec react-blocks index, so this change cannot red them. objectui was not edited.

Verification (union re-run at final commit b3716338e)

  • pnpm --filter @objectstack/lint testTest Files 81 passed (81), Tests 2275 passed | 5 skipped (2280)
  • pnpm --filter @objectstack/spec exec vitest run src/ui/react-blocks.test.tsTests 11 passed (11)
  • pnpm --filter @objectstack/spec check:generated✓ All 14 generated artifacts are up to date. (includes check:api-surface and check:react-blocks, on a dist rebuilt from this branch)
  • pnpm --filter @objectstack/spec typecheck / pnpm --filter @objectstack/lint typecheck — both exit 0 (run on the byte-identical tree committed as b3716338e)
  • node scripts/pm/dispatch-gates.mjs (no paths; derived at 90e7072e0, stderr names this repo/tree) → 27 matched families. Cheap named families run locally, all exit 0 with their own verdict lines (e.g. check-nul-bytes: OK (scanned 6519 text file(s) …), ✓ check-adr-0087-registration: this PR adds no declared-breaking changeset): nul-bytes, engine-double-contract, where-matcher, cross-package-test-inputs, query-options-erasure, doc-authoring, role-word, skill-compatibility, skill-frame-sync, spec-parsed-alias, published-files, test-source-alias, type-source-resolution, slot-lookup, merge-driver, objectui-changeset, changeset-gate-self-tests, adr-0087-registration, changeset-no-major, empty-changeset, plugin-teardown-shape.
  • Declared narrowings (CI owns the rest): check-dev-prereqs reds locally with its own "The workspace is not built — 1 unmet precondition, not a list of problems" (64/67 packages have no dist in an affected-only-built worktree; CI runs it after a full build). check:type-check-debt --re-measure likewise needs the full workspace closure built; the per-package tsc --noEmit runs above cover the typecheck risk of the new test files. Repo-wide pnpm lint (eslint) is CI-owned.

skills/** readings (governed surface — generated files only, human merge)

  • skills/objectstack-ui/contracts/react-blocks.contract.json: 537 → 559 lines (net +22, generated)
  • skills/objectstack-ui/references/react-blocks.md: 115 → 117 lines (net +2, generated)
  • Whole-package SKILL.md sum (all SKILL.md under skills/): 10506 → 10506 (unchanged — no prose expansion)

This diff touches skills/**, a governed surface (Prime Directive 14): human merge only — do not queue, arm auto-merge, or flip out of draft.

Deviation

packages/lint/src/index.ts (one line) is outside the claim's named file list: the rule-id-barrel-exports gate compels every new rule id constant to be exported from the barrel, and its failure text prescribes exactly this edit. Declared here as the same-round file-surface amendment.

Generated by Claude Code


Generated by Claude Code

…ata-tier spelling, deprecate-first
Canonical: data={{ provider: 'object', object }} and type (ListViewSchema's
own props, surfaced via dataProps). objectName / viewType stay published as
deprecated aliases for the deprecation window; the lint warns on each use
(react-prop-deprecated), accepts either spelling for the required binding,
and resolves field props against the object bound by whichever is present.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rxnd8cyFnoU8V5y21PaTsy
…el-exports gate)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rxnd8cyFnoU8V5y21PaTsy
@github-actionsgithub-actionsBot added size/m documentation Improvements or additions to documentation protocol:ui tests tooling labels Aug 24, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/lint, @objectstack/spec, touching 15 documentable anchor(s).

16 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: node scripts/docs-audit/affected-docs.mjs --json 88b9d749a1eb76b53966b0081006d2c5bccb341d.

4 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails.

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/lint/src/index.ts) — pages documenting those are invisible to this run
  • 1 anchor(s) matched too much of the corpus to be a work list: objectName (literal, 33 pages)
  • 4 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 45 of 222 client-bound route-ledger rows — the other 177 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run: node scripts/docs-audit/affected-docs.mjs --bridge-coverage

Coarse fallback — 126 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 88b9d749a1eb76b53966b0081006d2c5bccb341dpackageMentionDocs.

Which tree this was computed on

This run read content/docs from 92e38ae47469294de72163ecd00bed574dc5ecee — the merge of head b3716338ef8ed9faff3443a125618856314076a0 into base 88b9d749a1eb76b53966b0081006d2c5bccb341d, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 92e38ae47469294de72163ecd00bed574dc5ecee && git checkout 92e38ae47469294de72163ecd00bed574dc5ecee
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 88b9d749a1eb76b53966b0081006d2c5bccb341d b3716338ef8ed9faff3443a125618856314076a0 && git checkout -B drift-repro 88b9d749a1eb76b53966b0081006d2c5bccb341d && git merge --no-ff b3716338ef8ed9faff3443a125618856314076a0
node scripts/docs-audit/affected-docs.mjs --json 88b9d749a1eb76b53966b0081006d2c5bccb341d

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 88b9d749a1eb76b53966b0081006d2c5bccb341d → pass the list as
args.docs, on the commit named under Which tree this was computed on.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationprotocol:uisize/mteststooling

Projects

None yet

3 participants

@os-warren@os-zhuang@claude