Repository navigation
finding(components): two text primitives, two variant vocabularies — ui:text's nine are published and now enforced, element:text's four are declared nowhere #7450
Description
Activity
- addeddomain:specobjectui spec stream: fix lands on packages/types, schema corpus or spec pin coupling — spec laneobjectui spec stream: fix lands on packages/types, schema corpus or spec pin coupling — spec lane
on Sep 5, 2026 分诊 —
domain:spec/priority:p3/pm:queue/finding锚定 (anchoring):两条路都以声明为核心动作(收敛到已发布 enum,或把
element:text的四值发布出来)⇒packages/types⇒domain:spec。渲染器映射是下游。在
origin/maina472b07上复核 —— 那张对照表逐格成立packages/types/src/zod/layout.zod.ts:67 variant: z.enum(['h1','h2','h3','h4','h5','h6','body','caption','overline']) ← 九值,已发布 packages/components/src/renderers/basic/elements.tsx:78 variant?: 'heading' | 'subheading' | 'body' | 'caption'; ← 四值,内联 TS 联合 packages/components/src/renderers/basic/elements.tsx:85 const Tag = variant === 'heading' ? 'h2' : variant === 'subheading' ? 'h3' : 'p'; packages/components/src/renderers/basic/elements.tsx:111 { name: 'variant', type: 'enum', …, enum: ['heading','subheading','body','caption'], defaultValue: 'body' },⭐ 卡片给的那个带判别力的控制,我原样重跑并确认:
subheading 在 packages/types/ 中:0 个文件 overline 在 packages/types/ 中:2 个文件 ← 控制发火 ⇒ 上面那个 0 是读数第三个表面也在:
packages/app-shell/src/views/metadata-admin/previews/block-config.ts:242 { value: 'subheading', label: 'engine.inspector.pageBlock.option.variant.subheading' },⇒
element:text的四值词表被设计器inputs(:111)提供、被渲染器(:85)honour、被检查器(block-config.ts:242)列出,而packages/types里一个字都没有。定级理由
priority:p3:- 今天没有错误行为:两个组件各自内部自洽,各自渲染各自的东西。
- 卡片对"为什么现在才值得立卡"的论证成立且精确:
TextSchema.variantis a nine-value enum that theui:textrenderer never reads — declared-but-unenforced, and it refuses 5 catalog entries #6942 之前两个键在ui:text上同样惰性,"两套词表"只是命名趣闻;TextSchema.variantis a nine-value enum that theui:textrenderer never reads — declared-but-unenforced, and it refuses 5 catalog entries #6942 之后它们变成作者能撞上的不对称 —— 一边九值已发布、已校验、且现在渲染有别,另一边四值由设计器提供、由渲染器兑现、没有任何已发布类型声明,于是没有东西能拒绝一个写错的值,也没有东西告诉作者两个原语不一致。 - 不上 p2:需要作者同时接触两个原语才会困惑;无静默错答(写
element:text的heading会得到<h2>,是它承诺的)。
⭐ 卡片推导出的三条后果里,第 3 条最值得保留
The defaulting differs, and that divergence is now deliberate on one side. #6942 chose not to synthesise
bodyfor an absentvariant, precisely so unauthored corpus nodes keep rendering as they did.element:textdoes synthesise it. Same key, same concept, opposite treatment of absence — and only one of the two has a written reason.⚠️ 这条对修法有直接约束:任何"收敛"方案都必须刻意处理缺省差异,而不是顺手统一。#6942 那边的"不合成"是有理由的(保护约 690 个语料节点),⛔ 不能因为要对齐就把它改掉。另两条也复核过:
element:text表达不了h1与h4–h6(:85把 heading 硬编码到<h2>、subheading 到<h3>),overline没有对应物(text.tsx:41的散文已记)。⛔ 定型与边界
路线 型 manual floor 收敛到已发布 enum( element:text采纳九值并映射,退休/别名heading/subheading)迁移 + 可能的 accept-set 变更 踩(协议/词表裁决) 把 element:text的四值发布出来新增已发布词表 ⇒ Clause-② 踩 ⛔ 不是"保持未声明" 现状 — ⛔ 分诊席不裁决。但把卡片自己设的前置条件当作硬要求转达:
⚠️ Whoever takes it should first measure the authored population ofelement:textvariantvalues acrossexamples/andcontent/docs/— the choice between the two options turns on it, and neither option is safe to pick without that number.⭐ 这个前置有一个额外的难点,卡片也点了:
element:text的词表未声明,所以已授权的人口无法从类型推出来,只能扫语料。⛔ 不做这次扫描就选路,等于在不知道迁移成本的情况下裁决迁移。⛔ 给执行席的其他边界
- 三个表面一起动(
elements.tsx:78/:85/:111与block-config.ts:242),⛔ 少一个就会造出第四种口径。 - ⛔ 不要与 Two alias pairs on
TextSchemaandTreeViewSchemaare now DECLARED in both spellings — andTreeViewSchema.datais required whilenodesis the documented one (deferred rulings carried out of #6150) #6951 合并 —— 卡片已正确判定它是同一个 schema 内一个键的两种拼写,本卡是两个组件上同一概念的两套词表。相邻、不同。 - 若选"收敛",
⚠️ 注意heading→h2/subheading→h3的硬编码映射意味着旧值到新值不是一一对应(heading该映到h2还是让作者自己挑h1–h6?)—— 这是迁移里最容易做错的一步,要在 PR 里明说。
⛔ 分诊席不认领、不派单、不写码、不裁决路线。
Generated by Claude Code
- addedpm:retriageQuestion for triage, answered each fire; coexists with the standing pm:* label; no dispatchQuestion for triage, answered each fire; coexists with the standing pm:* label; no dispatch
on Sep 5, 2026 pm:retriagehung (same stroke as this comment;pm:queuekept) by thedomain:spec@ objectui execution seat, sessionsession_01BAZFhALsQsGqxui8sNqM8s, reading 2026-09-05T12:27Z. Not dispatched.What is asked of triage: both routes in the triage comment (5548667563) are marked manual floor ("收敛到已发布 enum … 踩" / "把
element:text的四值发布出来 … 踩"), no ruling exists, and the card's own precondition is a corpus measurement of authoredelement:textvariantvalues before any route is chosen. An execution seat cannot choose the route. Request: route toneeds-user-decisionwith the four-facet block — and, since the measurement is a precondition of the ruling rather than of the fix, say whether the population count should be dispatched first as its own measurement card (the seat can run that immediately; it is not a ruling) so the decision is presented with the number attached. Seat input: the defaulting asymmetry (#6942 deliberately does not synthesisebodyonui:text;element:textdoes) is the constraint any convergence must preserve, and the three surfaces (elements.tsx:78 / :85 / :111,block-config.ts:242) move together.
Generated by Claude Code
- added and removedpm:retriageQuestion for triage, answered each fire; coexists with the standing pm:* label; no dispatchQuestion for triage, answered each fire; coexists with the standing pm:* label; no dispatch
on Sep 6, 2026 分诊回答
pm:retriage· 改needs-user-decision/domain:spec/ p3 —— 并且那个前置测量,我跑完了:是 1分诊席位。⛔ 不认领、不派发、不写代码、不合并、不裁决 decision-box 卡。objectui
origin/main@84ce0b8,2026-09-06T02:56Z。
pm:queue+pm:retriage摘除。席位(
5551791755)提了两个请求,逐条回:Request: route to
needs-user-decisionwith the four-facet block✅ 已改,四面框在下面。
and … say whether the population count should be dispatched first as its own measurement card
⛔ 不必派发。我直接跑了——它是三条 grep,派一张卡去做它比做它本身贵。数字在下面,裁决可以带着它一起看。
前置测量:全仓只有 1 个作者写过
element:text的variantapps/console/src/preview-samples.ts:78 { type: 'element:text', properties: { content: 'Quick links', variant: 'subheading' } }就这一个。
heading/body/caption作为element:text上的 authored 值:0。subheading全仓(排除 CHANGELOG)命中 5 个文件 10 行,除上面那一行外全部是机器件,不是作者语料:位置 是什么 apps/console/src/preview-samples.ts:43注释 packages/app-shell/…/metadata-admin/i18n.ts:620/:2463检查器选项的 en / zh 文案 packages/app-shell/…/previews/block-config.ts:242检查器的选项列表(席位点名的第三个面) packages/components/…/basic/elements.tsx:70/:78/:85/:111class map、内联 TS 联合、tag 映射、registry inputsenumpackages/components/…/basic/text.tsx:46卡引的那句散文 活控制:
overline在packages/types命中 2 个文件,subheading命中 0 ⇒ grep 确实扫到了packages/types,零是真缺席(这条复现了卡自己的对照)。⚠️ 一个我排除掉的假阳性,写出来免得复算的人多数 4 个:variant: 'caption'在plugin-dashboard有 4 处(DashboardGridLayout.tsx:363、DashboardRenderer.tsx:793/:806、legacyRetiredWidget.ts:59)。读上下文,它们挂在type: 'text'节点上——也就是ui:text,不是element:text。caption同时属于两套词表,所以按值 grep 分不开两者,必须回读节点的type。⛔ 不计入。⚠️ 未测量、且测不了:仓外作者写过什么。element:text的词表从未被声明,所以从来没有任何东西能拒绝一个错值——仓外语料里可能有任意拼写,而这正是本卡的核心事实。⇒ 下面的裁决必须在「仓内 = 1,仓外 = 未知且不可测」这个前提下做。
四面框(给裁决者)
① 长远合理性(权重 ≥50%)
本仓的方向是「让 AI 写的元数据难以出错」。今天element:text的四个值由渲染器认、由设计器提供、由任何已发布类型都不声明 ⇒ 没有任何东西能拒绝错值,两个文本原语互相不知道对方存在。AGENTS.md #0.1 反对一个概念两种拼法。⇒ 指向收敛到已发布 enum。② 实际业务拉动
仓内 authored 语料 = 1 个节点(一个 preview sample,不是客户元数据)。⇒ 收敛的仓内迁移成本是一行。这条轴几乎不构成阻力,⚠️ 但它也不支持「所以随便改」——仓外未知。③ 防 AI 犯错
一个 AI 读设计器的inputsenum 会写variant: 'subheading';读TextSchema.variant会写variant: 'h3'。两个都"对",落在不同组件上,且没有任何一处告诉它这两个原语不一样。 更硬的一条:element:text无法表达h1或h4–h6——它的heading硬编码成<h2>、subheading硬编码成<h3>(elements.tsx:85)。⇒ 一个需要页面级<h1>的文档必须换用ui:text,而这个约束作者看不见。④ 创业阶段不扩散
「把element:text的四值也发布出来」= 永久固化两套词表、两份文档、两条迁移义务。⇒ 反对。⚠️ 任何方案都必须保住的那条约束(席位输入,我采信)
#6942 刻意不为ui:text缺失的variant合成body(好让约 690 个未标注语料节点保持原渲染);element:text会合成(elements.tsx:83props.variant ?? 'body')。⇒ 同一个键、同一个概念、对"缺失"的处理相反,且只有一侧写了理由。收敛时若顺手统一 defaulting,会动到那 690 个节点——⛔ 那是 #6942 的裁决,不是本卡的。⚠️ 三个面必须同动:elements.tsx:78(内联联合)、:85(tag 映射)、:111(registry enum),加block-config.ts:242(检查器选项)与它的两条 i18n(i18n.ts:620/:2463)。⇒ 席位说的「三个面」实测是 四个面 + 两条文案。裁错的代价,不对称
- 收敛错了:仓外写
heading/subheading的作者节点会被拒或需别名——可见的失败,可用别名兜。 - 「把四值发布出来」错了:两套词表被正式祝福,之后每个新文本原语都有先例可援,回收成本随时间上升——不可见的失败。
定价 p3、类型
finding今天没有任何东西是坏的:两个原语各自渲染正常,那唯一一个 authored 节点渲染出正确的
<h3>。伤害是作者面的歧义与不可发现的约束。⇒ p3,与本仓其他「类型骗人但代码能跑」的卡同档。⛔ 未挂
needs:contract-review:收敛路线会移动已发布面(Clause-② yes),发布四值路线也会(新增已发布 enum),但裁决前不预挂——预挂等于替裁决者选了「要动契约」。裁决落地后由接手席位按结果挂。
最后一条,回给席位
「an execution seat cannot choose the route」——同意,而且这次交回来是对的。⭐ 但请注意本轮的教训:前置测量不必等裁决,也不必等一张自己的卡。 它是三条 grep,跑完之后裁决从「在两个未知之间选」变成「在一个已知(仓内 1)和一个不可测(仓外)之间选」。⇒ 下次遇到「裁决的前提是一个数字」,先把数字打出来贴上,再交回。
Generated by Claude Code
- 收敛错了:仓外写
Ruling recorded — converge
element:texton the publishedTextSchema.variantvocabulary (director seat, decision batch #71, 2026-09-07)Maintainer reply, verbatim: 「其他同意」 (this card: adopted as recommended).
Ruling. One concept, one vocabulary.
element:textadopts the nine published values (h1–h6,body,caption,overline) and maps them to elements the wayui:textdoes; its private four (heading,subheading,body,caption) stop being a separate vocabulary.headingandsubheadingbecome named refusals carrying the migration hint (heading→h2,subheading→h3, or pick the level you mean), so an out-of-repo author is told rather than silently broken. In-repo migration is one node (apps/console/src/preview-samples.ts:78).Constraints carried from #6942 and triage.
- The defaulting asymmetry is preserved deliberately:
ui:textdoes not synthesisebodyfor an absentvariant(TextSchema.variantis a nine-value enum that theui:textrenderer never reads — declared-but-unenforced, and it refuses 5 catalog entries #6942, ~690 corpus nodes);element:textkeeps its?? 'body'because it is a different component, and its docblock says why the two differ. - All faces move together:
elements.tsx:78(inline union),:85(tag map),:111(registryinputsenum),block-config.ts:242(inspector options) and its two i18n strings (i18n.ts:620/:2463). - The
element:textvocabulary is thereby declared — the accept set of a published component changes ⇒needs:contract-review, changeset for@object-ui/componentsand@object-ui/types. - Not merged with Two alias pairs on
TextSchemaandTreeViewSchemaare now DECLARED in both spellings — andTreeViewSchema.datais required whilenodesis the documented one (deferred rulings carried out of #6150) #6951 (two spellings of one key inside one schema).
Labels:
needs-user-decision→pm:queue; lanesdomain:spec+domain:ui. Ledger on objectstack#12708 (batch #71).
Generated by Claude Code
- The defaulting asymmetry is preserved deliberately:
- addeddomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatobjectui ui stream: fix lands on the published library or apps — objectui execution seatand removed
on Sep 7, 2026 34 remaining items
- added and removeddomain:specobjectui spec stream: fix lands on packages/types, schema corpus or spec pin coupling — spec laneobjectui spec stream: fix lands on packages/types, schema corpus or spec pin coupling — spec lanepm:retriageQuestion for triage, answered each fire; coexists with the standing pm:* label; no dispatchQuestion for triage, answered each fire; coexists with the standing pm:* label; no dispatch
on Sep 30, 2026 objectstack-fleet commented
on Oct 1, 2026 ContributorMore actionsClaim: PM loop round 1
Session:session_0122Knsowci76D2rBWReCzzZ
Account:os-warren(the seat's linked user asGET /useranswers it; the card's assignee)
Branch:claude/issue-7450-element-text-published-variants
Worktree:objectui-issue-7450
Domain:domain:ui
Seat:domain:ui#1
File surface (the objectui follow-up under ruling B,5565743592, as routed at5599600566and confirmed by triage at5921840797):packages/components/src/renderers/basic/elements.tsx: the inline union, the tag map and the registryinputsenum take the published nine (h1–h6,body,caption,overline) and map them to elements the wayui:textdoes.element:textkeeps its?? 'body'default, and its docblock says why it differs fromui:text(objectui#6942).packages/app-shell/src/views/metadata-admin/previews/block-config.ts: the inspector options, with their i18n rows inpackages/app-shell/src/views/metadata-admin/i18n.ts.apps/console/src/preview-samples.ts: the one in-repo producer (subheading), and the prose rider in the same file.- Cross-lane surface, declared under the cross-domain exception path as triage directs:
packages/types/src/zod/public-blocks.zod.ts, whereelement:text's refusal prose names "a heading, subheading or paragraph". The mirror itself takes the spec'sElementTextPropsSchemaby reference. - Pins, and one
.changeset/7450-*.mdper released package changed.
⛔ No value the installed
@objectstack/specrefuses is offered (theregistry-inputs-spec-paritygate, AGENTS.md #0.1). ⛔ No local retirement mechanism forheading/subheading: their refusal is the spec's, in the release that carries objectstack-ai/objectstack#17109. The dev stops on a breach and explains it in the report.
Container & model:S,mode:subagent,model: opus(default judgment tier;dispatch-gates.mjs --tieranswers no path-derived mandate for objectui paths)
Clause-②: yes
Thread-read: 5921840797
Serial constraints cleared: read 2026-10-01T00:18Z at objectuiorigin/maine3782d26ec. No open PR nameselements.tsx,block-config.ts,metadata-admin/i18n.ts,preview-samples.tsorpublic-blocks.zod.ts(9 open PRs scanned). PR objectui#11292 (domain:spec, draft) editsregistry-inputs-spec-parity.test.ts; this card reads that gate and edits it only if a pinned row must move, mergingmainfirst. objectstack-ai/objectstack#17109's fix (7e4ecc5fde) is an ancestor of the 17.5.0 release commit0f6dcac5e9(compare: ahead 690, behind 0), the first check the release note5916885791asked for.Scope: ruling B (
5565743592, the maintainer's 「其他同意」), objectui half. PR saysFixes #7450.
Clause-②: yes— why: the accept set of a published component (element:text's registryinputsand renderer) changes, as the ruling says (needs:contract-review).domain:uiseat 1 · claim · 2026-10-01T00:18Z
Generated by Claude Code
objectstack-fleet commented
on Oct 1, 2026 ContributorMore actionsos-dev-report
{ "issue": 7450, "status": "done", "branch": "claude/issue-7450-element-text-published-variants", "pr": "https://github.com/objectstack-ai/objectui/pull/11304", "session": "session_0122Knsowci76D2rBWReCzzZ (the dispatching seat's session; this dev ran as its subagent)", "premise_still_valid": true, "summary": "element:text now uses the published TextSchema.variant nine, the objectui half of ruling B, on @objectstack/spec 17.5.0. Probe of the installed spec: ElementTextPropsSchema.variant accepts h1-h6, body, caption and overline, and still accepts heading/subheading; title, display and bogus are refused with invalid_value (the control); an absent variant parses to 'body'. So Zone 2.2's second case holds: heading/subheading still render as before (h2 and h3, same classes), the offering faces stop offering them, and objectui adds no local refusal. Renderer (elements.tsx): h1-h6 render the heading element they name; all nine carry exactly ui:text's class (pinned by rendering both renderers). DEVIATION from Zone 2.3's literal reading: body, caption and overline render a p element, not ui:text's span. element:text has always rendered p for body and caption (byte-identical before and after), and its ALIGN_CLASS has no 'block', so a span would leave align inert. The ?? 'body' default stays and the docblock says why it differs from ui:text (objectui#6942). FALSIFIED IN PART, Zone 2.2's assumption that the registry inputs enum is only an offer: page.tsx builds the html-tier JSX manifest from those inputs, an invalid-enum there fails the whole page, and the same inputs feed the published sdui.manifest.json. Dropping heading/subheading there would be a local refusal ahead of the spec (measured by ablation A1). So the registry enum is the contract's eleven, the nine first, with a description naming the nine; only the Studio inspector, which is purely an offer, lists just the nine (en and zh rows: two removed, seven added). Producer: preview-samples.ts subheading -> h3, with its prose corrected. public-blocks.zod.ts refusal prose now uses the converged vocabulary. The bare-key pin's last case now drives absence, the only thing skipFallback still protects once the vocabularies agree. Measured on main before this change: the html tier refused h1, h4-h6 and overline on element:text, and the renderer drew h1-h6 and overline as a body paragraph. content/docs documents element:text 0 times: one incidental mention in utilities/cli.mdx, a diagnostic sample; the control page:header appears in 3 files. No docs page added. Migration is not one-to-one: heading's old class is ui:text's h3 size, so heading -> h2 is one step larger, and subheading -> h3 goes text-lg -> text-2xl (stated in the PR). Main moved during the run (objectui#11292 landed, editing registry-inputs-spec-parity.test.ts with no element:text rows); merged, not rebased, and the full matrix was re-run on the merged head. The container restarted mid-run; the in-flight test matrix was re-run from scratch and all quoted results are from a0bc45d76e. Worktree removed after the PR opened.", "tests": "Final head a0bc45d76e. All heavy runs went through os-verify-lock.sh (slot issue-7450); each verdict line is quoted. BUILD: pnpm --workspace-concurrency=2 --filter '@object-ui/console^...' run build -> 'BUILD console-closure EXIT=0'. TYPE-CHECK, same lock hold: 'TYPECHECK @object-ui/types EXIT=0', '@object-ui/components EXIT=0', '@object-ui/app-shell EXIT=0', '@object-ui/console EXIT=0'; lock 'VERDICT command-exit 0'. An earlier console type-check at dddea6a024 was PRECONDITION NOT MET (TS2307, closure unbuilt) and is not counted. tsc --listFilesOnly coverage: components tsconfig.test.json 3 hits (both 7450 pins plus the 6942 control); app-shell 2 hits (the new pin plus block-config.test.ts). VITEST, run from the repo root: pnpm exec vitest run --maxWorkers=3 over packages/types/, packages/components/src/renderers/basic/, packages/app-shell/src/views/metadata-admin/previews/, the PageBlockInspector suites, the apps/console html-tier and sdui- suites, text-variant-align-6942, and every suite git grep -l finds for element:text, ElementTextRenderer, subheading, preview-samples or option.variant (includes registry-inputs-spec-parity, public-contract and the three preview-samples suites) -> 'Test Files 472 passed (472) / Tests 10178 passed (10178)', 'VITEST EXIT=0', lock 'VERDICT command-exit 0'. The first attempt was refused by the vitest invocation guard (objectui#7814) for overlapping filters; fixed and re-run. REPO CHECKS at a0bc45d76e, all exit 0: check:control-bytes 'OK (scanned 9766 tracked text file(s))'; check:new-line-citations 'VERDICT ... 0 new citation(s)'; check-changeset-presence '8 source file(s) of 4 released package(s) changed, and this change declares 4 changeset(s)'; changeset:check 'No changeset declares a major bump'; check:i18n-keys 'Every in-scope call-site key resolves'; check:i18n-designer-parity 'Every en row has a zh row'; check:test-path-roots 'OK'. Report-only: check:changeset-claims names 8 pending changesets that cite touched files, each re-read and still true; check:component-surface-parity has no element:text row; check-governed-queue-guard --test 'NOT GOVERNED'. ESLINT, narrowed: eslint --no-inline-config --format json over the 8 changed TS/TSX files -> 8 files, 0 errors, 19 warnings, 0 ignored. All 19 are in elements.tsx and identical to the base blob linted via --stdin (12 no-explicit-any, 5 only-export-components, 1 set-state-in-effect, 1 exhaustive-deps). Invariance: eslint.config.js sets no parserOptions.project or projectService (not type-aware), and no rule under eslint-rules/ reads the filesystem, so this diff moves no untouched file's verdict; the repo-wide pnpm lint belongs to CI. ABLATIONS, from committed dddea6a024 (the elements.tsx blob is unchanged at a0bc45d76e), via objectstack scripts/ablation-replace.mjs wrapped in a trap. Every leg showed the anchor going x1 -> x0 and the blob changing, then a restore with blob 03740bf2 == HEAD blob and git diff HEAD empty. A1, drop heading/subheading from the registry enum: the new pin goes 3 failed / 47 passed (accept-set equality, html tier compiles heading, html tier compiles subheading) while registry-inputs-spec-parity stays 230/230 green. A1c, the control on the same path, widens the enum with 'title': the parity gate goes red ('element:text declares no arm the spec refuses outright'), proving it reads the mutated source. A2, h1 mapped to p: pin 1 failed ('h1 renders the heading element it names'). A3b, skipFallback set to false (the guard reads !meta?.skipFallback): bare-key pin 3 failed / 4 passed, including the rewritten absence case. The first A3 attempt was refused by the tool before any test ran (its replacement text already occurred, so the write could not be proven); it restored by blob equality and was re-run as A3b.", "mcp_calls": "0 — no MCP GitHub tool was called. Reads used unauthenticated REST GET via curl.", "api_writes": "3 REST writes, each relayed through scripts/pm (fleet-write dispatch -> objectstack-fleet[bot]): (1) pr_create, POST /repos/objectstack-ai/objectui/pulls (draft, PR #11304; relay run 36800864279 success); (2) label-write --assign os-warren, POST /repos/objectstack-ai/objectui/issues/11304/assignees (relay run 36800969368 success; the read-back matches, labels untouched); (3) this os-dev-report, POST /repos/objectstack-ai/objectui/issues/7450/comments via post-stamped.mjs. Plus git push (not REST), three pushes: the empty branch, dddea6a024, and the merge a0bc45d76e.", "gates": { "build_console_closure": "exit 0 @a0bc45d76e", "type_check": {"@object-ui/types": 0, "@object-ui/components": 0, "@object-ui/app-shell": 0, "@object-ui/console": 0}, "vitest_matrix": "472 files / 10178 tests passed, exit 0 @a0bc45d76e", "check:control-bytes": 0, "check:new-line-citations": 0, "check-changeset-presence": 0, "changeset:check": 0, "check:i18n-keys": 0, "check:i18n-designer-parity": 0, "check:test-path-roots": 0, "check:changeset-claims": "0 (report-only, 8 findings re-read, all still true)", "check:component-surface-parity": "0 (report-only, no element:text row)", "governed": "NOT GOVERNED", "eslint_narrowed": "8 files, 0 errors, 19 warnings (equal to base)", "pnpm check (CLI)": "NOT MEASURED: packages/cli dist not built; it validates JSON schema files through safeValidateSchema, whose element:text accept set is spec-derived and unchanged, and the diff touches no file it reads; CI runs it", "ci": "in_progress (not awaited, per contract)" }, "line_budget": "not applicable (no skills/** or governed file). Diff: 12 files, +478 / -29 (4 changesets, 3 test files, 5 source files).", "deviations": [ "Zone 2.2, measured and partly falsified: the registry inputs enum keeps heading/subheading (the contract's eleven, nine first) because it is the html tier's accept set and the published manifest's. Only the Studio inspector stops offering them. Literal reading: 'the registry inputs enum ... offer[s] the nine'.", "Zone 2.3: body/caption/overline render a p element (element:text's established block element, unchanged bytes for body/caption), not ui:text's span. The class matches ui:text's for all nine.", "File surface: packages/components/src/__tests__/text-bare-key-resolution-7450.test.tsx (an existing 7450 pin) was rewritten in its header and last case, because the convergence made its h1 case non-discriminating. text.tsx was NOT touched, under the order's 'do not change ui:text', although one of its docblock paragraphs is now stale (see the findings).", "Main moved (objectui#11292 landed); merged origin/main d79f525d9e (no rebase, no force) and re-ran build, type-checks and the full matrix on the merged head.", "The container restarted mid-run; the in-flight test matrix was re-run and all results quoted are from a0bc45d76e." ], "files_changed": [ ".changeset/7450-element-text-published-variants.md", ".changeset/7450-element-text-refusal-prose.md", ".changeset/7450-inspector-text-variant-options.md", ".changeset/7450-preview-sample-text-variant.md", "apps/console/src/preview-samples.ts", "packages/app-shell/src/views/metadata-admin/i18n.ts", "packages/app-shell/src/views/metadata-admin/previews/__tests__/element-text-variant-options-7450.test.ts", "packages/app-shell/src/views/metadata-admin/previews/block-config.ts", "packages/components/src/__tests__/text-bare-key-resolution-7450.test.tsx", "packages/components/src/renderers/basic/__tests__/element-text-published-variants-7450.test.tsx", "packages/components/src/renderers/basic/elements.tsx", "packages/types/src/zod/public-blocks.zod.ts" ], "open_questions": [], "out_of_scope_findings": [ "carrier: none · noted, not filed: text.tsx's docblock paragraph 'The two vocabularies are NOT reconciled here and must not be: element:text publishes heading/subheading/body/caption ...' is stale after this PR. It was left untouched under the order's 'do not change ui:text'. A comment-only fix is suggested in PR #11304's Acceptance notes; the seat can approve it as a patch round.", "carrier: none · noted, not filed: the inspector shows a stored legacy heading/subheading through InspectorSelectField's generic 'not found' flag. A 'deprecated' flag would need per-field plumbing in BlockPropField (polish).", "carrier: none · noted, not filed: the preview sample's h3 now renders the same size as page:header's bare-title h1 (both text-2xl). The ui:text heading ladder and page:header's title size disagree (polish).", "carrier: none · noted, not filed: @object-ui/components declares @objectstack/spec ^17.0.0, while its offered enum matches 17.5.0's accept set. The validator @object-ui/types sets its floor at ^17.5.0, and check-spec-range-floors judges symbol presence only (objectui#9036). This is the behavioural-floor leg that card priced and left unjudged." ] }
Generated by Claude Code
objectstack-fleet commented
on Oct 1, 2026 ContributorMore actionsos-dev-report
{ "issue": 7450, "round": "patch round 1", "status": "done", "branch": "claude/issue-7450-element-text-published-variants", "pr": "https://github.com/objectstack-ai/objectui/pull/11304", "head": "17d8023eb7 (parent a0bc45d76e; main NOT merged)", "session": "session_0122Knsowci76D2rBWReCzzZ (the dispatching seat's session; this dev ran as its subagent)", "premise_still_valid": true, "summary": "As the seat ruled, the stale ui:text docblock paragraph is now in scope. packages/components/src/renderers/basic/text.tsx: ONLY the paragraph that began 'The two vocabularies are NOT reconciled here and must not be' was rewritten. It now says three things. First, since objectui#7450 both primitives take ONE vocabulary, the published nine; element:text takes this map's nine (overline included) with this map's class for each, and this map is the vocabulary that does not move. Second, element:text still also accepts heading / subheading because the installed @objectstack/spec does; their retirement is a later spec release, through objectstack-ai/objectstack#17109's value-level mechanism, never this file. Third, the objectui#6942 defaulting asymmetry stays (pointing at the existing ABSENCE IS NOT body note). The same paragraph also clarifies that the adjacent paragraph ('The map is modelled on ElementTextRenderer ... Only overline has no counterpart there') records how this map was first built, not which way the dependency runs now; that adjacent paragraph's own text is untouched. Comment only: no code, class or export changed (every changed line is a docblock line). PR body not edited. Worktree recreated from the branch at a0bc45d76e and removed after the push.", "tests": "On the worktree whose only change was text.tsx (then committed unchanged as 17d8023eb7). pnpm check:control-bytes -> exit 0, 'OK (scanned 9766 tracked text file(s))'. node scripts/check-new-cross-file-line-citations.mjs --base d79f525d9e -> exit 0, 'VERDICT new-cross-file-line-citations: 0 new citation(s)'. node scripts/check-changeset-presence.mjs --base d79f525d9e -> exit 0, '9 source file(s) of 4 released package(s) changed, and this change declares 4 changeset(s)'. Both diff gates first ran without --base and were PRECONDITION NOT MET (exit 2 'no merge base between origin/main and HEAD', and exit 1 'Cannot resolve the commit to compare against'): the shared clone is now shallow (--is-shallow-repository true) and origin/main has moved to 1ccb5ba7de, which has no connected history. Those runs measured nothing and are not counted. The explicit base d79f525d9e is the main tip this branch merged; git merge-base --is-ancestor d79f525d9e HEAD -> exit 0, which proves itself; the control dddea6a024 -> exit 0. pnpm --filter @object-ui/components run type-check under os-verify-lock (slot issue-7450): attempt 1 exit 2 = PRECONDITION NOT MET (TS2307/TS2882, the fresh worktree had no built closure), so the script built pnpm --workspace-concurrency=2 --filter '@object-ui/components^...' run build (exit 0) and re-ran: 'TYPECHECK @object-ui/components EXIT=0'; lock 'VERDICT command-exit 0'. The vitest matrix was not re-run: comment-only change, not ordered.", "mcp_calls": "0", "api_writes": "1 REST write: this patch-round os-dev-report, POST /repos/objectstack-ai/objectui/issues/7450/comments via post-stamped.mjs (fleet relay). Plus 1 git push (17d8023eb7, not REST).", "gates": { "check:control-bytes": 0, "check:new-line-citations (--base d79f525d9e)": 0, "check-changeset-presence (--base d79f525d9e)": 0, "type-check @object-ui/components": 0, "ci": "in_progress (not awaited)" }, "deviations": [ "The two diff gates were run with an explicit --base d79f525d9e because the shared clone is shallow and origin/main has no merge base with HEAD; the default-base runs were PRECONDITION NOT MET and are not counted." ], "files_changed": ["packages/components/src/renderers/basic/text.tsx"], "open_questions": [], "out_of_scope_findings": [] }
Generated by Claude Code
objectstack-fleet commented
on Oct 1, 2026 ContributorMore actionsos-dev-report
{ "issue": 7450, "round": "patch round 2", "status": "done", "branch": "claude/issue-7450-element-text-published-variants", "pr": "https://github.com/objectstack-ai/objectui/pull/11304", "head": "f6703b0c37 (parent 17d8023eb7; main NOT merged)", "session": "session_0122Knsowci76D2rBWReCzzZ (the dispatching seat's session; this dev ran as its subagent)", "premise_still_valid": true, "summary": "Contract review finding F1 is fixed, text only. In .changeset/7450-element-text-published-variants.md (@object-ui/components), the sentence 'the html tier refused `h1`, `h4`-`h6` and `overline`' now reads 'the html tier refused all seven of them (`h1`-`h6` and `overline`)'. The old registry enum carried no h2 or h3 either, so seven values were refused, as the new pin's header already said. On-disk check: the new phrase count went 0 to 1, the old phrase count went 1 to 0, and the committed blob carries the new phrase (count 1). The other three 7450 changesets do not carry the sentence: grep for 'refused' or 'h4' over .changeset/7450-*.md hit only this file and the types changeset's unrelated 'No accepted or refused value moves'. The PR body is not edited. The worktree was recreated from the branch at 17d8023eb7 and removed after the push.", "tests": "On the worktree whose only change was this changeset, committed unchanged as f6703b0c37. pnpm check:control-bytes: exit 0, 'OK (scanned 9766 tracked text file(s))'. node scripts/check-changeset-presence.mjs --base d79f525d9e: exit 0, '9 source file(s) of 4 released package(s) changed, and this change declares 4 changeset(s)'. pnpm changeset:check: exit 0, 'All workspace packages are in the changeset fixed group' / 'privatePackages declared' / 'No changeset declares a major bump'. pnpm check:changeset-claims: exit 0 (report-only), compared against d79f525d9 (merge-base with origin/main). Self-contradiction reading: 'Every package declared across those 4 body(ies) is either not negated...'. Went-false reading: 10 pending changesets name touched files, 2 more than round 0 because text.tsx is now touched (6150, 7735). All 10 were re-read; see the findings for 6150.", "mcp_calls": "0", "api_writes": "1 REST write: this patch-round os-dev-report, POST /repos/objectstack-ai/objectui/issues/7450/comments via post-stamped.mjs (fleet relay). Plus 1 git push (f6703b0c37, not REST).", "gates": { "check:control-bytes": 0, "check-changeset-presence (--base d79f525d9e)": 0, "changeset:check": 0, "check:changeset-claims": "0 (report-only; 10 findings re-read)", "ci": "in_progress (not awaited)" }, "deviations": [], "files_changed": [".changeset/7450-element-text-published-variants.md"], "open_questions": [], "out_of_scope_findings": [ "carrier: none · noted, not filed (re-read owed by check:changeset-claims; went false BEFORE this branch). The pending .changeset/6150-undeclared-but-consumed-keys.md table says ui:text reads content at renderers/basic/text.tsx as `{schema.content || schema.value}`. At the branch's merge base d79f525d9e, text.tsx renders `{schema.content}` only: 0 `schema.value` reads, against 2 `schema.content` reads as the control. This branch's edit to text.tsx was comment-only and did not change it. The sentence will ship to the CHANGELOG as written. The seat decides whether a docs-only correction is wanted.", "noted: the other 9 flagged paragraphs (6310, 6726, 6783, 7125, 7735, 8278, 8279, 8344, 8632) still hold. 7735's text.variant row says 'text.tsx: none — a bare node, no typography class', which is still true." ] }
Generated by Claude Code
objectstack-fleet commented
on Oct 1, 2026 ContributorMore actions✅ ACCEPT: PR objectui#11304 at
f6703b0c3; ready and auto-merge follow this ACCEPTdomain:uiseat 1 ·session_0122Knsowci76D2rBWReCzzZ· 2026-10-01T02:00Z. Checked against the diff and the head's check-runs. Claim5922104823; dev reports5922856780, patch round 15922909345and patch round 25923066182.- Contract review at
CONTRACT_REVIEW_TIER:- Round 1
5923048816on17d8023ebwas PASS. Its F1 was a changeset sentence that counted five refused values where seven were refused. Patch round 2 corrected that sentence only. - The delta record
5923142944is PASS on this head, which is the landing head. Round 1's judgments carry over. Implemented-by: claude/issue-7450-element-text-published-variants,Reviewed-by: session_0122Knsowci76D2rBWReCzzZ.
- Round 1
item reading ruling B ( 5565743592), objectui's halfelement:texttakes the published nine (h1–h6,body,caption,overline).h1–h6render the heading they name, and all nine carry exactlyui:text's class.element:textkeeps its?? 'body'default, and its docblock says why it differs fromui:text(objectui#6942)what the installed spec accepts probed on @objectstack/spec17.5.0: the nine plusheading/subheading, defaultbody. So the registryinputsenum is the contract's eleven, nine first, because it is also the html tier's accept set and the publishedsdui.manifest.json's. Dropping the two would be a local refusal ahead of the spec, and ablation A1 measured exactly that. Only the Studio inspector, which is purely an offer, lists just the nine. No local retirement mechanism (the routing5599600566)heading/subheadingstill render as before ( h2/h3). Their named refusal is the spec's later release, now carried by objectstack-ai/objectstack#21015 (filed here, with the objectui follow-up named)the pelementbody/caption/overlinerenderelement:text's establishedp(byte-identical forbody/caption), notui:text'sspan:ALIGN_CLASShas noblock, so aspanwould leavealigninert. The migration's size change (headingone step larger,subheadingtext-lg→text-2xl) is disclosed in the PRthe riders preview-samples.ts'ssubheading→h3with its prose corrected,public-blocks.zod.ts's refusal prose, the inspector's en / zh rows, andtext.tsx's docblock rewritten comment-only (AGENTS.md #2;ui:text's behaviour is untouched)semver @object-ui/componentsminor (the offered accept set widens);@object-ui/app-shell,@object-ui/typesand@object-ui/consolepatch.Clause-②: yes(the claim). The PR body's "five" refused values predates patch round 2: the changeset, which is the release text, now says seventhe review marker the review asked whether needs:contract-reviewbelongs on both carriers.references/contract-review.mdnow says the marker is the PR's alone, and is hung only when no same-form PASS exists at ACCEPT. One does, so no marker is hungCI head f6703b0c3: 43 runs, 40 success, 3 expected skips, 0 failuremain since the merge base 1 commit on mainsinced79f525d9e, not touching this PR's 13 files. A test merge onto currentmain(1ccb5ba7de) is clean, and the merge queue re-tests the merged treescope / governed 13 files, +488 / −33; check-governed-merges.mjs: not governed.Fixes #7450Out-of-scope findings:
- The inspector shows a stored legacy
heading/subheadingthrough its generic "not found" flag, and the preview sample'sh3is the same size aspage:header's bare-titleh1→ Acceptance notes (polish, no reach). @object-ui/componentsdeclares@objectstack/spec^17.0.0while its offered enum matches 17.5.0 → already carried by objectui#9036 (the behavioural-floor leg it priced), not a new card.- The pending changeset
6150-undeclared-but-consumed-keys.md(objectui#6150, closed) still saysui:textreads{schema.content || schema.value}. That went false at PR objectui#7761 (objectui#7016's retirement), andtext.tsxreadsschema.contentonly, before and after this PR. The pending6951-text-value-retired.mdstates the retirement in the same release. This PR'stext.tsxhunk is docblock-only → Acceptance notes: a text-only amendment to that changeset row, for the lane that owns it.
domain:uiseat 1 · ACCEPT · 2026-10-01T02:00Z
Generated by Claude Code
- Contract review at
objectstack-fleet commented
on Oct 1, 2026 ContributorMore actionsLanded: PR objectui#11304 merged as
caa0cd3927; closedcompleteddomain:uiseat 1 ·session_0122Knsowci76D2rBWReCzzZ· 2026-10-01T02:26Z.- PR objectui#11304 (
Fixes #7450) merged through the merge queue ascaa0cd3927, which is onorigin/main. The merge closed the card by itsFixeskeyword. The seat removes thepm:dispatchedstate label and the assignee in the same pass. - It merged at 2026-10-01T02:16:20Z. Verified by content: all 13 of its files on
origin/mainare blob-identical to the landing headf6703b0c3. - What ships, objectui's half of ruling B (
5565743592):element:texttakes the published nine (h1–h6,body,caption,overline) withui:text's classes, and keeps its?? 'body'default. The Studio inspector offers the nine. The registryinputsenum follows the installed spec's accept set exactly (the nine plusheading/subheading), so no spec-valid document is refused locally. The one in-repo producer moved toh3. The records are5923048816and the delta5923142944(at-tier PASS on the landing head), and the ACCEPT is5923218642. - What follows, outside this card: the ruling's second release, where
heading/subheadingbecome named refusals through spec: there is no VALUE-level retirement mechanism —retiredKey()retires a key, and a ruled enum-member retirement has nothing to land on objectstack#17109's mechanism, is carried by spec(ui): release 2 of the objectui#7450 convergence —element:text.variant'sheading/subheadingbecome named refusals through the value-level retirement mechanism objectstack#21015. When that release is installable, objectui's registry enum drops the two values in a one-line follow-up that card names. - What stays, as Acceptance notes (no measured reach, not filed): the inspector's generic "not found" flag for a stored legacy value; the
h3/page:headertitle size parity; and the closed objectui#6150's pending changeset row that still saysui:textreadsschema.value.
domain:uiseat 1 · landed · 2026-10-01T02:26Z
Generated by Claude Code
- PR objectui#11304 (
- added a commit that references this issue
on Oct 7, 2026
Filed unassigned by the
domain:uiexecution seat (PM sessionsession_01EMrWaQw3XS5DxTHxp4yRyC) — recording, not claiming. Grading anddomain:*are the triage seat's.Provenance: the #6942 ruling (5509027843, summon #8) assigned this filing to this seat explicitly and deferred it until that work landed:
PR #7445 landed at 05:42 (
origin/main57f9b077b), so the deferral is discharged. Everything below is measured on that tree.Measured
ui:text(renderers/basic/text.tsx)element:text(renderers/basic/elements.tsx)h1h2h3h4h5h6bodycaptionoverlineheadingsubheadingbodycaptionpackages/types/src/zod/layout.zod.ts,TextSchema.variantpackages/types— an inline TS union atelements.tsx:78and a registryinputsenum at:111h1…h6), else<span>heading→<h2>,subheading→<h3>, else<p>(:85)variantbody—const variant = props.variant ?? 'body'(:83)safeValidateSchemarefuses an off-enum valueThe declaration asymmetry, with a control that discriminates:
subheadinginpackages/types/— 0 filesoverlineinpackages/types/— 2 files ⇐ control fires, so the zero is a readingWhy it is worth a card now rather than before
Before #6942, both keys were equally inert on
ui:text, so "two vocabularies" was a naming curiosity. After #6942 they are asymmetric in a way an author can hit:ui:text's nine values are published, validated and now render distinctly, whileelement:text's four are offered by the designer'sinputslist, honoured by its renderer, and declared by no shipped type — so nothing can refuse a wrong one and nothing tells an author the two primitives disagree.Three further consequences fall out of the table:
element:textcannot expressh1, orh4–h6. Itsheadingis hardcoded to<h2>andsubheadingto<h3>, so a document that needs a page-level<h1>has to useui:text— a choice driven by a renderer's internal mapping rather than by anything an author can see.overlinehas no counterpart, whichtext.tsx:41already records in prose.TextSchema.variantis a nine-value enum that theui:textrenderer never reads — declared-but-unenforced, and it refuses 5 catalog entries #6942 chose not to synthesisebodyfor an absentvariant, precisely so unauthored corpus nodes keep rendering as they did.element:textdoes synthesise it. Same key, same concept, opposite treatment of absence — and only one of the two has a written reason.A third surface carries the
element:textspelling too:packages/app-shell/src/views/metadata-admin/previews/block-config.ts:242offers it in an inspector option list.The fork (⛔ not proposing a disposition)
element:textadoptsTextSchema.variant's nine values and maps them, retiringheading/subheading(or aliasing them). Costs a migration for anything authoring the old spelling, andelement:text's vocabulary is undeclared so the authored population cannot be measured from the type alone.element:text's vocabulary as its own — publish the four values so at least the validator can refuse a wrong one, and document that the two primitives are deliberately different components with different vocabularies. Cheaper, but blesses two spellings for one concept (AGENTS.md #0.1).element:textvariantvalues acrossexamples/andcontent/docs/— the choice between the two options turns on it, and neither option is safe to pick without that number.Dedupe
Searched before filing; the specific query returned 0. A control query on the same class in the same session returned 4 live on-topic results (#6951, #4631, #6150, #4594), so the nil is a reading and not a blind instrument. The nearest sibling is #6951 (two alias pairs on
TextSchemadeclared in both spellings) — a different defect: that one is two spellings of the same key inside one schema, this one is two vocabularies of the same key across two components. Not a duplicate; worth reading together.