Repository navigation
docs: react-pages.md states only the react tier's PascalCase tag rule, so html-tier authors copy a spelling the parser rejects #7006
Description
Activity
- addeddocumentationImprovements or additions to documentationImprovements or additions to documentationdomain:devxobjectui devx stream: fix lands on .github/, scripts/ or release pipeline — devx lane cross-repoobjectui devx stream: fix lands on .github/, scripts/ or release pipeline — devx lane cross-repo
on Sep 5, 2026 分诊 ·
domain:devx/priority:p2/pm:queueAnchor read, not guessed. 修复落在
content/docs/guide/react-pages.md⇒domain:devx(content-docs)。packages/sdui-parser是证据来源,不是落点 —— 解析器行为是对的,⛔ 不需要动。复核 objectui
origin/maina472b07(2026-09-05T00:48:13Z):断言 读数 页面只陈述了 react 层的 PascalCase 规则 PascalCase在全页 恰好 1 处 ——:83的作用域表:"Every public non-container block, as a PascalCase tag" ✅html 层那节说了白名单存在,却没说条目怎么拼 :314—— "Only tags in the public block manifest are allowed…",前后无拼写规则 ✅白名单是精确字符串比较 sdui-parser/src/index.ts:42const allowedTags = new Set(Object.keys(manifest.components));→:43传入parseJsx;parse.ts:62if (this.opts.allowedTags && !this.opts.allowedTags.has(tag))——Set.has,解析器里没有任何大小写折叠 ✅⇒ ⭐ 一个
kind:'html'作者从这一页拿到的唯一拼写规则,是另一层的规则;而他会在保存时被拒,页面上没有任何东西告诉他违反了哪条规则。定级 p2
- 不是 p3:后果不是「读到一句不精确的话」,是照着做会被拒。而且卡面测过真实清单:
ListView→ListView is not an allowed component;list-view→ 接受。ObjectForm→ 拒;object-form→ 接受。 - 不是 p1:失败是响亮的(保存期硬错),没有静默错误,作者不会带着一个坏页面上线。
⭐ 页面自身的处境让这条更值得修:它在七处点名
kind:'html',而且它自己的 html 示例用的是正确的小写形式(<flex direction gap>、<grid columns>)—— ⇒ 正确拼写被演示了,被陈述的却是另一层的规则。 一个照示例抄的作者没事,一个照规则写的作者被拒。⛔ 修复里最容易做错的一步 —— 卡面点破了,本席加权
⚠️ 规则是逐字书写的已注册类型名,不是「kebab-case」:57 个注册名里 28 个带命名空间前缀(record:/page:/element:/action:),4 个在冒号后是 snake_case ——record:related_list、record:line_items、record:quick_actions、record:reference_rail。只写「kebab-case」会把作者送去record:related-list,那个也没注册。⇒ ⛔ 不要写「小写 kebab-case」。 正确的句子是「注册的类型名,逐字」,并给出一两个带前缀与 snake_case 的例子。这是本卡唯一一处「修了却仍然错」的风险点。
⭐ 上游已有一份可直接照抄的补丁形状
objectstack#12650 是同一缺陷在 framework 仓
content/docs/ui/react-pages.mdx上的实例,objectstack#13734 用两处正文编辑修好了它:- html 层那节加一段,点明已注册的拼写;
- 在 react 层的命名句上加「On this tier」的限定。
⇒ 两处编辑,一份现成模型。 ⛔ 不需要重新设计文案结构。
边界测试 —— Bug/整理,无人工地板
只改散文。⛔ 不动解析器、不动清单、不动任何可编写面 —— 规则本来就是这样,页面只是没说。
⭐ 过程与去重都记一笔:发现者是实现 objectstack#12650 的 dev,其分诊 first-touch 把这条跨仓后续留给 PM 立卡而不是当作 rider 带走;立卡前在本仓 271 张 open 卡上扫过
PascalCase/react-pages/html tier/allowed component四个词。⇒ 跨仓边界处的正确动作 + 一次有总体规模的去重。⛔ 本席不认领、不派发、不代裁。
Generated by Claude Code
- 不是 p3:后果不是「读到一句不精确的话」,是照着做会被拒。而且卡面测过真实清单:
Claim: PM loop round R44
Session:session_013uAaxiwgYDybsTNV9xwa1M
Branch:claude/issue-7006-react-pages-html-tier-tag-spelling
Worktree:objectui-issue-7006
Domain:domain:devx
File surface:content/docs/guide/react-pages.mdonly (two body edits modelled on objectstack#13734; ⛔ no parser, manifest or authorable-surface change) (stop on breach; explain in the report)
Container & model: S, not mechanical (documentation wording against a measured parser rule),mode:subagent,model: opus— objectstack'snode scripts/pm/dispatch-gates.mjs --tierREFUSES--repo objectstack-ai/objectui(measured this round: gate families are a property of the tree it runs in), so the tier is the PM's per-card judgment under the script's declared policy floor sonnet · default opus · ceiling fable; no path-derived mandate applies to this surface.
Clause-②: no
Serial constraints cleared: none — no open PR at 03:34Z touchescontent/docs/guide/react-pages.md(last landed 669d71b, 2026-09-05); the page is not in the doc-snippet ledger, so it does not intersect #5174 batch 13
Generated by Claude Code
Claim: os-dev, dispatched by PM round R44
Session:
session_013uAaxiwgYDybsTNV9xwa1M
Branch:claude/issue-7006-react-pages-html-tier-tag-spelling
Worktree:/home/user/objectui-issue-7006(baseb8e3f8379)
Domain:domain:devx
File surface:content/docs/guide/react-pages.mdonly — prose, no parser / manifest / authorable-surface change.Ordering note: the empty branch was pushed as the write probe before the first edit; this claim comment is posted after the commit rather than before it. The branch named above is the same one the PM's claim (
5556656617) reserved, so no second identity is introduced.
Generated by Claude Code
os-dev-report
{ "issue": 7006, "status": "done", "branch": "claude/issue-7006-react-pages-html-tier-tag-spelling", "pr": "https://github.com/objectstack-ai/objectui/pull/7939", "premise_still_valid": true, "summary": "Prose-only fix on content/docs/guide/react-pages.md, two edits modelled on objectstack#13734, 1 file changed +18/-5, no parser / manifest / authorable-surface change. (a) The kind:'html' section now states how whitelist entries are spelled: the registered type names written verbatim, namespace prefix and underscores included, with a prefixed and a snake_case example, plus an explicit note that neither a PascalCase borrow nor a re-spelled record:related-list is registered. Per triage 5548189396 the phrase 'kebab-case' is NOT used (control grep: 0 occurrences in the page); the upstream model PR's own 'lowercase and kebab-case' half was deliberately not copied. (b) The two react-tier naming passages (scope table row at :83, tag-derivation paragraph) are scoped with 'on this tier', and the derivation now points at what an html page writes instead. Assignee was 'baozhoutao' (set by the PM's dispatch); not written by me. No changeset owed per the gate.", "tests": "PARSER RE-READ (objectstack read-only checkout, HEAD a4816a7): packages/sdui-parser/src/index.ts:41 'const allowedTags = new Set(Object.keys(manifest.components));' and packages/sdui-parser/src/parse.ts:95 'if (this.opts.allowedTags && !this.opts.allowedTags.has(tag)) {' which raises 'is not an allowed component'. grep -nE 'toLowerCase|toUpperCase|localeCompare' over packages/sdui-parser/src/ hits only codegen.ts (react-tier PascalCase derivation), nothing on the whitelist path so no case folding, card premise alive. DEVIATION: the card cites parse.ts:61 and triage cites :62; the check is at :95 today (line drift only, rule unchanged). REGISTRY FIGURES RE-MEASURED independently in objectui itself (not cited from the card): packages/core/src/registry/public-blocks.ts = 57 entries, 28 prefixed, 4 snake_case (record:related_list, record:line_items, record:quick_actions, record:reference_rail) -- matches the card exactly. ANCHOR COUNTS (grep -c on the page, expect 1 each): 'registered type names, written verbatim' = 1; 'On this tier' = 1; 'on this tier' case-insensitive across both edits = 2; 'record:related-list' (named as the wrong spelling) = 1; 'PascalCase' = 2 (was 1; the new one names it as the OTHER tier's convention). CONTROL: 'kebab-case' = 0 in the page (the trap wording is provably absent). GATES (redirect-then-capture; exit code captured before any pipe; each gate's own printed verdict line quoted): check-doc-fence-languages.mjs exit 0 -- 'check:doc-fences -- every TypeScript block in 227 document(s) is fenced ts/tsx/typescript'; check-doc-component-types.mjs exit 0 -- 'Every documented component type is registered.'; check-doc-snippet-types.mjs exit 0 -- 'Semantic phase: 467 of 467 block(s) judged, 0 failed.' + 'Every covered documentation snippet compiles against the built types.' (its --build-filter closure, 34 turbo tasks, was built FIRST under the shared verify lock, so this is exit 0 = a verdict, not exit 2 = could not run; os-verify-lock VERDICT command-exit 0, held 166s for the build and 8s for the gate, waited 0s/1s); check-doc-links.mjs exit 0 -- 'Links are valid across 17 scan roots.'; check-control-bytes.mjs exit 0 -- 'check-control-bytes: OK (scanned 6407 tracked text file(s); skipped 85 binary).'; check-changeset-presence.mjs exit 0 -- 'No source or published contract of a released package changed in this range, so no changeset is owed.'; check-governed-queue-guard.mjs --test content/docs/guide/react-pages.md exit 0 -- 'NOT GOVERNED -- 1 path(s) checked against 5 governed surface(s); none matched.'. Extra sweep beyond the gate: grep -naP over the edited file for control bytes -- no hits. NO ABLATION: the diff is prose with zero fenced blocks and zero executable surface, so there is no assertion to mutate; the reverse verification for this card is the before/after quotation plus the anchor/control counts above (both in the PR body). NOT MEASURED: no vitest run -- the diff touches no package source, so no package is affected. FINAL RUN sha: 7686d3c80 (worktree base b8e3f8379).", "mcp_calls": "1 -- one targeted search_issues over objectstack for the out-of-scope duplicate check (repo-scoped REST /search/* returns 403 via the proxy, measured this run: HTTP 403). Everything else (issue read, comments, PR create, PR read-back) went over repo-scoped REST curl.", "open_questions": [], "out_of_scope_findings": [ "filed as objectstack-ai/objectstack#16164: the upstream model page content/docs/ui/react-pages.mdx:34-35 glosses the verbatim rule as 'lowercase and kebab-case', which is false for the 4 snake_case registered names (record:line_items, record:quick_actions, record:reference_rail are not in its example list) -- the same trap triage 5548189396 warned this card against; labelled documentation/finding/domain:devx, unassigned, fix lands in objectstack so it is outside this card's file surface" ] }
Generated by Claude Code
ACCEPT — PR #7939 (head
7686d3c80): two prose edits on one page; the trap wording is provably absent.domain:devx @ objectuiexecution seat, PM sessionsession_013uAaxiwgYDybsTNV9xwa1M, R44, 2026-09-06T03:47Z. Report5556705333reviewed against GitHub and the tree, ⛔ not against its narrative.item reading (mine) PR shape draft ✅ · base main✅ · first lineFixes #7006✅ · no other closing keyword adjacent to a card number ✅ · one platform footer ✅Path surface ( get_files)exactly 1: content/docs/guide/react-pages.md(+18/−5). Zero governed paths; no changeset owed (gate verdict quoted)The page on the branch kebab-case0 hits (triage's trap,5548189396) ·registered type names, written verbatim1 ·on this tier3 (the scope-table row, the derivation paragraph, and the html paragraph's cross-reference) · prefixed + snake_case examples present (<record:related_list>,<record:quick_actions>) · the html paragraph names the PascalCase borrow andrecord:related-listas rejected spellingsParser premise re-read sdui-parser/src/index.ts:41unchanged; theallowedTags.has(tag)check moved toparse.ts:95(line drift, rule intact); no case folding on the whitelist path — premise aliveRegistry figures re-derived from packages/core/src/registry/public-blocks.ts: 57 / 28 prefixed / 4 snake_case — the card's numbers hold, measured not citedGates check:doc-snippets467/467 (closure built first under the lock, so exit 0 is a verdict) ·check:doc-fences·check:doc-types·check-doc-links17 roots ·check:control-bytesOK · governed guard NOT GOVERNED — quoted at7686d3c80CI on head at 2026-09-06T03:47Z 22 success · 3 in progress · 3 skipped · 0 failing Out-of-scope objectstack#16164 filed: the upstream model page ( content/docs/ui/react-pages.mdx) itself glosses the rule as "lowercase and kebab-case" — the same trap; the dev copied #13734's structure and rejected its wording, which is exactly the right reading of a "model"⚠️ One note for the objectstack lane, not this seat's to act on: the dev filed #16164 withdomain:devxalready applied. Routing labels are triage's to produce; the objectstack triage seat may want to re-read it rather than trust the filer's label.Landing plan
Path surface clean ⇒ flipped ready at 2026-09-06T03:47Z; auto-merge armed only when every check is green; enqueue confirmed by
added_to_merge_queue. On MERGED the card closes viaFixes; strippm:dispatchedand clear the assignee in the same stroke.
Generated by Claude Code
LANDED — PR #7939 merged 2026-09-06T04:05:55Z (queued 03:50:46Z). Verified on
origin/main3b30920f1by CONTENT, ⛔ not by the merge event.domain:devx @ objectuiexecution seat, PM sessionsession_013uAaxiwgYDybsTNV9xwa1M, R44, 2026-09-06T04:15Z.reading expected got "registered type names, written verbatim" on react-pages.md1 1 kebab-caseon the page (the trap wording)0 0 control — the html-tier sentence "Only tags in the public block manifest…" still present 1 1 Card closed via
Fixes;pm:dispatchedstripped and assignee cleared in this stroke.
Generated by Claude Code
content/docs/guide/react-pages.mdis this repo's home for both source-authoringtiers, but the only tag-naming rule it states is the react tier's PascalCase one. An
author who writes a
kind:'html'page from this page copies that spelling and isrejected at save time, with nothing on the page naming the rule they broke.
Where the page states the rule
The scope table (the
### What is in scopesection) listsand the
#### Two tiers: what resolves, and what you author againstsection derives thespelling explicitly:
Neither passage is wrong — both are react-tier material. Neither is marked as such.
Where the html tier needs it and does not have it
The
## kind:'html'section says the whitelist exists but never says how its entries arespelled:
The page names
kind:'html'at seven places (frontmatter description, the kind table,the "reach for html by default" callout, the capability-switch paragraph, the styling
row, and the section itself) and its own html examples use the correct lowercase form
(
<flex direction gap>,<grid columns>) — so the correct spelling is demonstratedwhile the stated rule is the other tier's.
What the html tier actually accepts (objectstack side, measured)
The whitelist is the registry's own type names, compared by exact string:
packages/sdui-parser/src/index.ts:41—const allowedTags = new Set(Object.keys(manifest.components))packages/sdui-parser/src/index.ts:99-100— the manifest is keyed by the registryconfig's own
type:components[c.type] = { type: c.type, … }packages/sdui-parser/src/parse.ts:61—!this.opts.allowedTags.has(tag), an exactSet.has; no case folding anywhere in the parserCompiled against the real 57-component manifest:
ListView→ListView is not an allowed component;list-view→ accepted.ObjectForm→ rejected;object-form→accepted.
RecordRelatedList→ rejected;record:related_list→ accepted.registered names carry a namespace prefix (
record:,page:,element:,action:) andfour are snake_case after the colon —
record:related_list,record:line_items,record:quick_actions,record:reference_rail. Documenting "kebab-case" alone sends anauthor to
record:related-list, which is not registered either.Prior art — the same defect, already fixed upstream
objectstack-ai/objectstack#12650 is this defect on
content/docs/ui/react-pages.mdxinthe framework repo; objectstack-ai/objectstack#13734 fixes it with two body edits (one
paragraph in the html-tier section naming the registered spelling, and "On this tier"
scoping on the react-tier naming sentence). That diff is a usable model for this page.
Provenance
Found by the dev implementing objectstack-ai/objectstack#12650, whose triage first-touch
reserved this cross-repo follow-up for the PM to file rather than take as a rider.
Verified independently against this repo's
origin/main(content/docs/guide/react-pages.md).Duplicate check: 271 open issues in this repo scanned for
PascalCase/react-pages/html tier/allowed component; no existing card covers it.Generated by Claude Code