Measured while implementing objectui#7115's part 3. The maintainer's 2026-09-01 ruling (comment 5494799307) chose fork (B): non-text types get no new carriage rows, and the docs stop authoring ${…} in keys that have none — 「文档教现实」. It named two sites in this file: progress.value (:388) and input.value (:453). Both are rewritten in the #7115 PR.
The ruling's enumeration came from the objectui#7015 census, which looked at type: "text" nodes only. Re-reading the file against the carriage map itself turns up six more occurrences of the same class, in the same file, that no census has covered.
The measurement
Read from the artifact, not from prose — expressionBindableTextKeysFor out of @objectstack/spec@17.2.0's built dist/ui/index.mjs, the same lookup SchemaRenderer.tsx:1139 consumes:
statistic -> [label, value, description]
card -> [title, description]
button -> [label]
badge -> []
progress -> []
input -> []
SchemaRenderer evaluates exactly four top-level channels: content (no type gate), the carriage keys for the node's type, the properties bag and the props bag, plus the condition keys (visibleOn / hiddenOn / disabledOn). Anything else written at a node's top level reaches the renderer as the characters the author typed.
The six occurrences, on main (line numbers as of 24d4a21ff)
| section |
key |
node type |
carriage rows for that type |
| Comparison Operators |
variant |
badge |
none |
| Scoped Data |
body |
card |
title, description — no body |
| Nested Ternary |
variant |
badge |
none |
| Status Badge |
text |
badge |
none |
| Status Badge |
variant |
badge |
none |
| Conditional Styling |
className |
card |
title, description — no className |
Note the Scoped Data block is a mixed case worth reading carefully: its sibling "title": "${item.name}" on the same card node IS a carriage key and does evaluate. Only body beside it does not, so the block half-works, which is the least visible way for this to be wrong.
Not claimed, deliberately
"items": "${users}" appears twice on list nodes. items is a data key, not one of the four text keys the carriage map governs, so whether the list renderer resolves it is a separate, unmeasured question. It is recorded here so the next reader does not have to re-derive that it was considered — not as part of the finding.
Why this is a card rather than more of #7115
The ruling is explicit about which sites it covers, and the seat that dispatched #7115 was explicit that anything the ruling did not cover is flagged, not folded in. The principle 「文档教现实」 clearly reaches these six, but applying it is a rewrite decision per section — a badge whose variant cannot be an expression needs the section to teach something else, exactly as progress and input did in #7115 — and that is what the ruling ruled on for two sites, not six.
⚠️ Scheduling note, carried over from the #7115 comment thread: objectui#4795's second half (build-time rejection of ${…} outside the carriage map) reddens every one of these when it lands.
Measured while implementing objectui#7115's part 3. The maintainer's 2026-09-01 ruling (comment 5494799307) chose fork (B): non-
texttypes get no new carriage rows, and the docs stop authoring${…}in keys that have none — 「文档教现实」. It named two sites in this file:progress.value(:388) andinput.value(:453). Both are rewritten in the #7115 PR.The ruling's enumeration came from the objectui#7015 census, which looked at
type: "text"nodes only. Re-reading the file against the carriage map itself turns up six more occurrences of the same class, in the same file, that no census has covered.The measurement
Read from the artifact, not from prose —
expressionBindableTextKeysForout of@objectstack/spec@17.2.0's builtdist/ui/index.mjs, the same lookupSchemaRenderer.tsx:1139consumes:SchemaRendererevaluates exactly four top-level channels:content(no type gate), the carriage keys for the node's type, thepropertiesbag and thepropsbag, plus the condition keys (visibleOn/hiddenOn/disabledOn). Anything else written at a node's top level reaches the renderer as the characters the author typed.The six occurrences, on
main(line numbers as of24d4a21ff)variantbadgebodycardtitle,description— nobodyvariantbadgetextbadgevariantbadgeclassNamecardtitle,description— noclassNameNote the Scoped Data block is a mixed case worth reading carefully: its sibling
"title": "${item.name}"on the samecardnode IS a carriage key and does evaluate. Onlybodybeside it does not, so the block half-works, which is the least visible way for this to be wrong.Not claimed, deliberately
"items": "${users}"appears twice onlistnodes.itemsis a data key, not one of the four text keys the carriage map governs, so whether the list renderer resolves it is a separate, unmeasured question. It is recorded here so the next reader does not have to re-derive that it was considered — not as part of the finding.Why this is a card rather than more of #7115
The ruling is explicit about which sites it covers, and the seat that dispatched #7115 was explicit that anything the ruling did not cover is flagged, not folded in. The principle 「文档教现实」 clearly reaches these six, but applying it is a rewrite decision per section — a
badgewhosevariantcannot be an expression needs the section to teach something else, exactly asprogressandinputdid in #7115 — and that is what the ruling ruled on for two sites, not six.${…}outside the carriage map) reddens every one of these when it lands.