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

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

claude added 2 commits August 24, 2026 11:38
…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-actions github-actions Bot 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.

@os-warren
os-warren marked this pull request as ready for review August 24, 2026 14:37
@os-warren
os-warren added this pull request to the merge queue Aug 24, 2026
Merged via the queue into main with commit 5383fa6 Aug 24, 2026
39 checks passed
@os-warren
os-warren deleted the claude/issue-11284-react-tier-vocab-converge branch August 24, 2026 14:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation protocol:ui size/m tests tooling

Projects

None yet

3 participants