Measured by the census that objectui#7851 landed (PR objectui#7868), on 59df1dab2. Filed rather than repaired: objectui#7851's ruling is that the census is the deliverable and ⛔ it does not repair other pages' sites. Recording, not claiming — grading and domain:* are the triage seat's.
The measurement
Read from the artifact, never from prose: expressionBindableTextKeysFor out of the built @objectstack/spec@17.2.0 dist/ui/index.mjs, the same lookup SchemaRenderer.tsx:1139 consumes. SchemaRenderer evaluates exactly four channels — content, the carriage keys for the node's own type, the properties and props bags — plus the eight condition keys. An expression written anywhere else reaches the renderer as the characters the author typed.
Corpus-wide: 204 json/jsonc fences, 204 parsed, 0 unparsed, 17 uncarried sites in 5 pages. Of those 17, 8 belong to objectui#7418 (guide/expressions.md, PR objectui#7847) and 1 to objectui#7680 (schema-rendering.md's chart.data). The 7 below belong to no open card.
The seven sites
| page |
line |
node |
key |
carriage rows for that type |
content/docs/guide/schema-rendering.md |
186 |
badge |
text |
none |
content/docs/guide/schema-rendering.md |
187 |
badge |
variant |
none |
content/docs/guide/schema-rendering.md |
207 |
alert |
variant |
none |
content/docs/guide/schema-rendering.md |
416 |
alert |
message |
none |
content/docs/guide/architecture.md |
257 |
badge |
text |
none |
content/docs/guide/architecture.md |
258 |
badge |
variant |
none |
content/docs/guide/layout.md |
511 |
page |
title |
none |
Line numbers are as of 59df1dab2 and will drift; locate by content.
Two of them are worse than "the expression does not evaluate"
PR objectui#7847 measured both through the real production renderers while repairing the same shapes one page over, so this is quoted rather than re-derived:
text is not a BadgeSchema key at all. BadgeSchema declares label / variant / icon / children, and renderers/data-display/badge.tsx renders schema.label. So "text": "${…}" on a badge renders an empty badge, with text=${…} leaking as a DOM attribute. Both badge.text rows above are that, not merely an unevaluated expression.
success / warning / info are not badge variants. The closed set is default / secondary / destructive / outline. Both badge.variant rows above author one of the invalid three. (⚠️ Not asserted for alert: that component's variant set was not measured here.)
The ruling that already applies
The maintainer's 2026-09-01 ruling on objectui#7115 chose fork (B): non-text types get no new carriage rows, and the docs stop authoring ${…} in keys that have none — 「文档教现实」. ⛔ Adding carriage rows for badge.text, alert.message or page.title is fork (A) and was rejected; that widens an authoring surface and would be a maintainer decision, not a docs fix.
PR objectui#7847 is the worked precedent for how each site is decided — per passage, not one blanket rule: a passage demonstrating binding moves onto a channel that carries (a text node's content, evaluated on every type; or a real carriage key on the same node; or the properties bag), and a passage where the non-carrying component is the subject becomes a literal plus prose naming the missing row.
⚠️ Worth reading before acting: schema-rendering.md's two hits sit under headings named "Conditional Expressions" and "Complex Logic", so those passages are demonstrating binding and cannot simply be de-expressed without teaching something else.
Considered and deliberately NOT claimed
content/docs/api/schema-reference.md:612 authors "condition": "${data.items.length > 0}" inside the ActionSchema example. The census reports it, and it is recorded here so the next reader does not have to re-derive that it was looked at: type: "action" is the action vocabulary, not an SDUI component node, and condition is evaluated by the action runner's own gate rather than by SchemaRenderer's node channels. It is not part of this finding. (check:doc-types' header measured at least seven vocabularies in this corpus that all spell the key type; the census inherits that gate's ruling that a global key-path discriminator does not converge, and prints "a hit is a candidate, not a verdict" under every findings list for exactly this reason.)
How to re-measure
node scripts/check-doc-expression-carriage.mjs
Report-only: it prints the census and exits 0. It also prints its own blind spot in three directions — parsed/unparsed per fence language, and whether any fence outside json/jsonc would have parsed as a JSON document holding a typed node — so the "7 sites" above is a bounded number rather than "7 is what I happened to find".
Related, not duplicates: objectui#7418 (guide/expressions.md, in flight as PR objectui#7847), objectui#7680 (the chart.data site on one of these same pages), objectui#7440 and objectui#7444 (the read-back half of the class, which this census does not answer), objectui#7851 (the census itself).
Filed by the os-dev seat working objectui#7851; session https://claude.ai/code/session_01KbJQ1y1J12nZxYzFWhP8Q3.
Generated by Claude Code
Measured by the census that objectui#7851 landed (PR objectui#7868), on
59df1dab2. Filed rather than repaired: objectui#7851's ruling is that the census is the deliverable and ⛔ it does not repair other pages' sites. Recording, not claiming — grading anddomain:*are the triage seat's.The measurement
Read from the artifact, never from prose:
expressionBindableTextKeysForout of the built@objectstack/spec@17.2.0dist/ui/index.mjs, the same lookupSchemaRenderer.tsx:1139consumes.SchemaRendererevaluates exactly four channels —content, the carriage keys for the node's own type, thepropertiesandpropsbags — plus the eight condition keys. An expression written anywhere else reaches the renderer as the characters the author typed.Corpus-wide: 204 json/jsonc fences, 204 parsed, 0 unparsed, 17 uncarried sites in 5 pages. Of those 17, 8 belong to objectui#7418 (
guide/expressions.md, PR objectui#7847) and 1 to objectui#7680 (schema-rendering.md'schart.data). The 7 below belong to no open card.The seven sites
content/docs/guide/schema-rendering.mdbadgetextcontent/docs/guide/schema-rendering.mdbadgevariantcontent/docs/guide/schema-rendering.mdalertvariantcontent/docs/guide/schema-rendering.mdalertmessagecontent/docs/guide/architecture.mdbadgetextcontent/docs/guide/architecture.mdbadgevariantcontent/docs/guide/layout.mdpagetitleLine numbers are as of
59df1dab2and will drift; locate by content.Two of them are worse than "the expression does not evaluate"
PR objectui#7847 measured both through the real production renderers while repairing the same shapes one page over, so this is quoted rather than re-derived:
textis not aBadgeSchemakey at all.BadgeSchemadeclareslabel/variant/icon/children, andrenderers/data-display/badge.tsxrendersschema.label. So"text": "${…}"on a badge renders an empty badge, withtext=${…}leaking as a DOM attribute. Bothbadge.textrows above are that, not merely an unevaluated expression.success/warning/infoare not badge variants. The closed set isdefault/secondary/destructive/outline. Bothbadge.variantrows above author one of the invalid three. (alert: that component's variant set was not measured here.)The ruling that already applies
The maintainer's 2026-09-01 ruling on objectui#7115 chose fork (B): non-
texttypes get no new carriage rows, and the docs stop authoring${…}in keys that have none — 「文档教现实」. ⛔ Adding carriage rows forbadge.text,alert.messageorpage.titleis fork (A) and was rejected; that widens an authoring surface and would be a maintainer decision, not a docs fix.PR objectui#7847 is the worked precedent for how each site is decided — per passage, not one blanket rule: a passage demonstrating binding moves onto a channel that carries (a
textnode'scontent, evaluated on every type; or a real carriage key on the same node; or thepropertiesbag), and a passage where the non-carrying component is the subject becomes a literal plus prose naming the missing row.schema-rendering.md's two hits sit under headings named "Conditional Expressions" and "Complex Logic", so those passages are demonstrating binding and cannot simply be de-expressed without teaching something else.Considered and deliberately NOT claimed
content/docs/api/schema-reference.md:612authors"condition": "${data.items.length > 0}"inside theActionSchemaexample. The census reports it, and it is recorded here so the next reader does not have to re-derive that it was looked at:type: "action"is the action vocabulary, not an SDUI component node, andconditionis evaluated by the action runner's own gate rather than bySchemaRenderer's node channels. It is not part of this finding. (check:doc-types' header measured at least seven vocabularies in this corpus that all spell the keytype; the census inherits that gate's ruling that a global key-path discriminator does not converge, and prints "a hit is a candidate, not a verdict" under every findings list for exactly this reason.)How to re-measure
Report-only: it prints the census and exits 0. It also prints its own blind spot in three directions — parsed/unparsed per fence language, and whether any fence outside
json/jsoncwould have parsed as a JSON document holding a typed node — so the "7 sites" above is a bounded number rather than "7 is what I happened to find".Related, not duplicates: objectui#7418 (
guide/expressions.md, in flight as PR objectui#7847), objectui#7680 (thechart.datasite on one of these same pages), objectui#7440 and objectui#7444 (the read-back half of the class, which this census does not answer), objectui#7851 (the census itself).Filed by the
os-devseat working objectui#7851; sessionhttps://claude.ai/code/session_01KbJQ1y1J12nZxYzFWhP8Q3.Generated by Claude Code