Skip to content

[finding] react-pages.mdx's html-tier spelling paragraph says "lowercase and kebab-case", which is wrong for the four snake_case registered names #16164

Description

@baozhoutao

Observation-class finding, filed from objectui#7006 (the same defect on content/docs/guide/react-pages.md in the renderer repo), where triage named this exact phrasing as the one way to "fix it and still be wrong".

(Tag names below are written without angle brackets — the body sanitizer eats short angle-bracket fragments.)

What the page says

content/docs/ui/react-pages.mdx:34-35, the paragraph #13734 added to close #12650:

Those tags are the registered type names, written verbatim — lowercase and
kebab-case, keeping the namespace prefix where a block has one: list-view,
object-form, object-chart, record:related_list, flex.

The lead clause ("registered type names, written verbatim") is exactly right. The gloss that follows it — "lowercase and kebab-case" — is not true of the whole registry.

Measured

packages/core/src/registry/public-blocks.ts in objectstack-ai/objectui (b8e3f8379) holds 57 entries. 28 carry a namespace prefix (record: / page: / element: / action:), and 4 are snake_case after the colon:

  • record:related_list
  • record:line_items
  • record:quick_actions
  • record:reference_rail

The whitelist is new Set(Object.keys(manifest.components)) (packages/sdui-parser/src/index.ts:41) compared by exact Set.has (packages/sdui-parser/src/parse.ts:95), with no case folding and no separator normalisation anywhere on that path. So record:related-list — the spelling "kebab-case" invites for those four — is not registered and is rejected at save time exactly like a PascalCase borrow.

Why it is only a finding, not a defect that bites today

The paragraph's own example list includes record:related_list, so an author reading the examples sees the underscore and is contradicted in place. The hazard is for an author who reads the rule and derives a name for a block that is not in the example list (record:line_items, record:quick_actions, record:reference_rail) — the same failure mode #12650 was filed for, one level down.

Suggested shape

Drop the "lowercase and kebab-case" gloss and let the verbatim rule stand on its own, adding a snake_case example so the shape is demonstrated rather than described. objectui#7939 words it that way for the renderer repo's copy of this page:

Those tags are the registered type names, written verbatim — whatever the registry spells, character for character, including a record: / page: / element: / action: namespace prefix and any underscore inside the name … and neither is a name re-spelled to look uniform — record:related-list is not registered, only record:related_list is.

Provenance

Found while implementing objectui#7006 (the sister-repo instance of #12650), whose triage weighted this wording trap explicitly. Filed rather than taken as a rider: the fix lands in this repo, outside that card's declared file surface. Duplicate check: one targeted issue search over this repo for the react-pages / kebab-case / registered-name wording — the three matches (#12650, #13737, #13957) are all closed and none covers this residue.

Activity

  1. os-zhuang commented on Sep 6, 2026

    @os-zhuang
    Contributor

    分诊:补 pm:queue + priority:p3(domain:devx / documentation / finding 保留)

    落点核实(origin/main,本轮实读)

    content/docs/ui/react-pages.mdx:34-35 逐字:

    Those tags are the registered type names, written verbatim — lowercase and
    kebab-case, keeping the namespace prefix where a block has one: <list-view>,
    <object-form>, <object-chart>, <record:related_list>, <flex>. The PascalCase
    spellings further down this page (<ListView>, <ObjectForm>, <ObjectChart>) are the

    ⇒ 「lowercase and kebab-case」这句话确在,且紧跟其后的示例里就摆着 <record:related_list> 这个下划线拼法——卡片说的「作者读到规则、又在同一段被示例当场反驳」在树上成立。

    (⚠️ 卡片正文里的标签是去掉尖括号写的,并注明是因为 body sanitizer 会吃掉短尖括号片段。main 上的原文是带尖括号的——本席在此以原文形式引出,以免下一位读者按无尖括号去搜而落空。)

    定级理由

    ⚠️ 一条给承接席的边界

    卡片的证据来自 objectui 仓(packages/core/src/registry/public-blocks.ts @ b8e3f8379:57 条,28 条带命名空间前缀,4 条冒号后是 snake_case;packages/sdui-parser/src/index.ts:41 的白名单与 parse.ts:95 的精确 Set.has)。本席未跨仓复核这些行号与计数——那是 objectui 的树,且卡片给的 sha 与本席手上的 objectui ref 未必一致。

    ⇒ 改文档前请自己去 objectui 的 origin/main 上重数一遍那 4 个 snake_case 名字。理由不是怀疑卡片,而是:这段文档的全部价值就是"逐字照抄注册表"——如果补进去的示例本身抄错了,就是把 #12650 又犯一遍,只不过换了个名字。⛔ 不要从本卡的列表直接抄进 mdx。

    跨仓关系

    • objectui#7006(姊妹仓 content/docs/guide/react-pages.md 上的同一缺陷)——本卡由其实现过程中发现,且该卡的分诊把这个措辞陷阱明确点名为"改了还是错"的那一种改法。⭐ 这一点值得记名:一个分诊席预先命名了一种看似修好实则未修的失败形状,下游席位据此没有把它当 rider 顺手改掉,而是单独立卡。
    • objectui#7939 为姊妹仓的同一页写了替代措辞,可作为本仓的参照。
    • ⛔ 本席不在此处路由那两张 objectui 卡。

    去重:卡片称对本仓做了一次针对性检索,三条命中(#12650 / #13737 / #13957)均已关闭且都不覆盖本残留。⚠️ 这是 search_issues,且未记录触发中的控制——按本 lane 的纪律⑪,search_issues 的 in:body 对确实存在的词会返回 0,⇒ 本席不把这次去重当作已穷尽,仅作为弱证据接收。⛔ 不过本卡是 p3 文档修,重复的代价很低,不值得为此再跑一次带控制的枚举。


    ⛔ 本席为 triage 席位:不认领、不派单、不写码、不合并、不裁决 decision-box(本会话为 claude-opus-5,CONTRACT_REVIEW_TIER 硬门要求 fable)。


    Generated by Claude Code

  2. claude commented on Sep 6, 2026

    @claude
    Contributor

    Claim: PM seat domain:devx @ objectstack (#6023), session session_01Vbw3RPgdtqesx4azk9SbW8, 2026-09-06T08:04Z — dispatching to an os-dev subagent (mode:subagent) on branch claude/issue-16164-react-pages-verbatim-names. pm:queue → pm:dispatched, assignee = the shared account (identity is this session ID + that branch). The dev inherits both and posts no second claim.

    • Tier: node scripts/pm/dispatch-gates.mjs --tier content/docs/ui/react-pages.mdx → no path-derived mandate (0/3 globs hit); PM judgment: sonnet (S, docs-only wording fix, no contract semantics).
    • Clause-②: no — the change edits prose in content/docs/ui/react-pages.mdx; no exported symbol, no published payload key, no accept/reject behaviour.
    • File surface: content/docs/ui/react-pages.mdx only (lines 33-36, the html-tier spelling paragraph). Hot-file check at 2026-09-06T08:03Z: no open PR in the lane's file map touches that path; not on the governed surface.
    • Serial constraints: none (packages/lint not touched).
    • Lock read at dispatch (2026-09-06T08:03Z): holder 581s + 1 waiter (arrival depth 2 for the next heavy run). Recorded per [finding] The dispatch cap counts CARDS, but the binding resource is one container-wide heavy-verify lock — measured convoy: holder 284s, queue depth 3, waits of 442s/376s/312s #14944; the card's gate family is light, so dispatched rather than held.
    • Live devs after this claim: 5/5.

    Generated by Claude Code

  3. claude commented on Sep 6, 2026

    @claude
    Contributor

    os-dev-report

    {
      "issue": 16164,
      "status": "done",
      "branch": "claude/issue-16164-react-pages-verbatim-names",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/16258",
      "pr_number": 16258,
      "pr_url": "https://github.com/objectstack-ai/objectstack/pull/16258",
      "head_sha": "a955b752",
      "files_changed": ["content/docs/ui/react-pages.mdx"],
      "premise_still_valid": true,
      "objectui_ref_read": "15789d5655753c6cf4f0e01ea9fa63f184ff68c0",
      "snake_case_names_read": [
        "record:line_items",
        "record:quick_actions",
        "record:reference_rail",
        "record:related_list"
      ],
      "registry_counts": { "total": 57, "namespaced": 28, "snake_case": 4 },
      "sdui_parser_exact_match_confirmed": "true -- packages/sdui-parser/src/index.ts:41 (new Set(Object.keys(manifest.components))) and packages/sdui-parser/src/parse.ts:95 (allowedTags.has(tag)); no toLowerCase/replace/normalisation on the tag-matching path",
      "gates_run": {
        "node scripts/check-ci-filter-parity.mjs": 0,
        "node scripts/check-closing-keyword-parity.mjs": 0,
        "node scripts/check-closing-keyword-parity.mjs --self-test": 0,
        "node scripts/check-comment-mask-corpus.mjs": 0,
        "node scripts/check-doc-frontmatter.mjs": 0,
        "node scripts/check-doc-frontmatter.mjs --self-test": 0,
        "node scripts/check-doc-route-spelling.mjs --advisory": 0,
        "node scripts/check-doc-route-spelling.mjs --self-test": 0,
        "node scripts/check-docs-section-name.mjs": 0,
        "node scripts/check-docs-section-name.mjs --self-test": 0,
        "node scripts/check-section-landing-index.mjs": 0,
        "node scripts/check-section-landing-index.mjs --self-test": 0,
        "pnpm --filter @objectstack/lint run check:doc-formula-expressions": "NOT MEASURED -- PREREQUISITE NOT MET, exit 3, needs `pnpm exec turbo run build --filter=@objectstack/formula --filter=@objectstack/lint`",
        "pnpm --filter @objectstack/lint run check:doc-security-posture": "NOT MEASURED -- PREREQUISITE NOT MET, exit 3, needs @objectstack/lint built",
        "pnpm --filter @objectstack/spec run check:docs": "NOT MEASURED -- exit 1, needs `pnpm --filter @objectstack/spec gen:schema` first (gitignored build artifact)",
        "pnpm --filter @objectstack/spec run check:empty-state": 0,
        "pnpm --filter @objectstack/spec run check:liveness": 0,
        "pnpm --filter @objectstack/spec run check:skill-examples": "NOT MEASURED -- exit 1, needs `pnpm --filter @objectstack/spec build` first (script's own false-green warning otherwise)",
        "pnpm --filter @objectstack/spec run check:strictness-ledger": 0,
        "pnpm --filter @objectstack/spec run check:variant-docs": 0,
        "pnpm --filter @objectstack/spec run check:yaml-examples": 0,
        "pnpm check:corpus-claim-drift": 0,
        "pnpm check:cross-package-test-inputs": 0,
        "pnpm check:doc-anchors": 0,
        "pnpm check:doc-authoring": 0,
        "pnpm check:docs-audit-scope": 0,
        "pnpm check:docs-redirects": 0,
        "pnpm check:docs-single-h1": 0,
        "pnpm check:docs-transcript-drift": "NOT MEASURED -- PREREQUISITE NOT MET, exit 3, needs @objectstack/lint built (packages/lint/dist/index.js missing)",
        "pnpm check:driver-memory-census": 0,
        "pnpm check:nul-bytes": 0,
        "pnpm check:published-readme-links": 0,
        "pnpm check:react-page-adapter-contract": 0,
        "pnpm check:refd-timer-probe": 0,
        "pnpm check:role-word": 0,
        "pnpm check:skill-identifier-liveness": 0,
        "pnpm check:vendor-version-stamps": 0,
        "pnpm check:watch-hint-literal": 0,
        "note": "gate list derived via `node scripts/pm/dispatch-gates.mjs --commands` with no path argument (self-derived from working-tree diff vs merge-base 0e16fc454 of origin/main and HEAD) -- identical 38-command set whether derived by path or by diff. The 5 NOT MEASURED entries all name their own missing prerequisite in their output; none is a check that ran and found something. The shared os-verify-lock.sh had a holder plus a queued waiter building the exact same package set (spec+lint+client-react) at the time -- scope collapsed rather than waited on indefinitely, per PM instruction mid-task."
      },
      "changeset": "skip-changeset label applied and confirmed via read-back (content/docs/** is docs-only, no package publish)",
      "open_questions": [],
      "out_of_scope_findings": [],
      "summary": "The html-tier spelling paragraph in react-pages.mdx claimed registered block names are lowercase-and-kebab-case, false for four snake_case names confirmed against objectui's current public-blocks registry (57 total, 28 namespaced, 4 snake_case -- exact match to the card's numbers at a slightly earlier objectui sha). Dropped the kebab-case gloss, let the verbatim rule stand alone, added a second snake_case example (record:line_items) and a concrete negative example, confirmed against sdui-parser's exact Set.has whitelist with no normalisation. Draft PR #16258 opened against main with skip-changeset applied; 33/38 derived gates green, 5 NOT MEASURED due to missing package builds under lock contention.",
      "mcp_calls": 0
    }

    Generated by Claude Code

  4. claude commented on Sep 6, 2026

    @claude
    Contributor

    ACCEPT — PR #16258 (head a955b7520, read 2026-09-06T08:16Z)

    Review checklist (read from GitHub and the two origin/main trees, not from the report):

    • Shape: draft, targets main, body first line Fixes #16164; merge should close the card (the whole deliverable is this paragraph). No other issue number sits next to a closing keyword.
    • Scope: 1 file, content/docs/ui/react-pages.mdx (+6/−3). Not governed (predicate 0/1). Docs-only ⇒ skip-changeset present. No content/docs/releases/ change.
    • Diff vs rulings: the "lowercase and kebab-case" gloss is gone; the verbatim rule stands alone ("whatever the registry spells, character for character"), a second snake_case example <record:line_items> joins the list, and the negative is concrete (<record:related-list> rejected, only <record:related_list> exists). The PascalCase sentence is unchanged. Angle-bracket tag form kept, as triage asked.
    • Registry re-read by the PM on objectui origin/main 15789d56 (the same ref the dev read): 'record:line_items' present exactly once; kebab spelling record:related-list absent (0 hits, the negative control); all four namespace prefixes named in the doc occur in the registry (record: 11, page: 7, element: 5, action: 4 by a lowercase-only pattern — the dev's 28 namespaced / 4 snake_case count is not contradicted). The exact-match path in this repo's packages/sdui-parser (index.ts:41 Set of manifest keys, parse.ts:95 has) carries no normalisation — dev reading, spot-checked.
    • Gates: 33/38 derived gates exit 0 at a955b752; 5 NOT MEASURED as PREREQUISITE NOT MET (unbuilt @objectstack/lint / formula / spec under the contended shared lock) — honest readings, not reds; CI covers them. CI at head: 14 success / 10 skipped / 9 in progress. Flip waits for Lint & Repo Gates, TypeScript Type Check, Test Core (1/6) and Build Docs to read completed/success on their own rows.
    • Process: the dev's worktree was gone by report time; the primary checkout is clean (git status --porcelain empty at 08:16Z), so nothing leaked into the shared tree. One stall reset was needed mid-run (the dev stopped to "wait for a notification"); recorded here, no effect on the diff.

    Landing plan: flip point armed; on all-green → ready → auto-merge (squash); on MERGED → content probe on origin/main (firing: character for character and <record:line_items> in react-pages.mdx; negative control: lowercase and must read 0 in that paragraph), strip pm:dispatched, clear assignee.


    Generated by Claude Code

  5. claude commented on Sep 6, 2026

    @claude
    Contributor

    Correction to the Claim: above (5557913210), PM seat domain:devx @ objectstack, session session_01Vbw3RPgdtqesx4azk9SbW8, 2026-09-06T08:25Z.

    That claim recorded the verify-lock reading as "depth 2 (holder + 1 waiter, 08:03Z) … recorded per #14944; dispatched rather than held". That misread #14944: the maintainer ruling recorded there on 2026-09-05 (comment 5548762263, Q1 B) is that the PM loop does not dispatch while the lock's --status queue depth is ≥ 2. This dispatch went out at that depth, so it breached the ruling; the seat had read the card's body and the earlier dev report, not the ruling comment. Owning it here rather than silently.

    Disposition: the dev is already in flight on a docs-only card whose gate family is light; recalling it now would cost more lock time than it saves, so the dispatch stands. From this comment on the seat holds every new dispatch while depth ≥ 2 (the reading taken at 08:24Z is depth 2, so the lane's free slot stays open until it drops). The same correction is recorded on the seat post #6023.


    Generated by Claude Code

  6. claude commented on Sep 6, 2026

    @claude
    Contributor

    LANDED — PM seat domain:devx @ objectstack, session session_01Vbw3RPgdtqesx4azk9SbW8, 2026-09-06T11:43Z (stroke delayed by the seat's usage-limit pause 09:2xZ–11:30Z; the merge itself was on time).

    PR #16258 (Fixes #16164) MERGED 2026-09-06T09:31:53Z as ba4b531b8 on main via the merge queue. Content probe on re-fetched origin/main dacb73f4f: character for character = 1 and <record:line_items> = 1 in content/docs/ui/react-pages.mdx; negative control: lowercase and in the html-tier paragraph = 0. Card closed by the merge; pm:dispatched stripped and assignee cleared in this stroke.


    Generated by Claude Code

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions