Measured while implementing #6783 (the five readProps() copies in packages/components/src/renderers/basic/), outside its fence — that card's file surface is packages/components/src/renderers/basic/ only — so filed separately. Observation-class in the same sense #6752, #6760 and #6783 were: nothing is known to depend on it today.
The fact, measured on 107babef6
packages/plugin-detail/src/renderers/record-alert.tsx:168:
function readProps(schema: any) {
const fromNested = (schema?.properties ?? {}) as any;
return { ...schema, ...fromNested };
}
?? only replaces null / undefined, so a degenerate properties passes straight into the object spread and is re-read as its own character indices — Object.keys({ ...'not-a-bag' }) is ["0","1","2","3","4","5","6","7","8"]. This is the identical hazard #6783 closed on the five renderers/basic/ copies, on the same channel (the config bag a renderer reads), in a different expression: this one merges { ...schema, ...schema.properties } rather than { ...schema.props, ...schema.properties }, so it has no props alias leg but does put the node's own keys underneath.
The alias-precedence-cross-channel.test.tsx docblock already names this site as the sixth member of the family, so it is known ground, not a new discovery — what is new is that #6783 has now converged the other five and left this one alone.
Blast radius, honestly bounded
Same denominator as the sibling cards: #6708's census walked every JSON document, every json doc fence and every TypeScript object literal and found zero authored nodes carrying a degenerate config bag. Latent shape, not a live failure.
And the same measurement #6783 recorded about its own five applies here: record-alert reads only named keys off the bag (props.title, props.body, props.severity, props.action?.label, props.dismissible, …) and never spreads it onto a DOM element, so today the indexed keys are computed and then dropped. Nothing rendered moves either way. What the guard would buy is what #6752 measured its own guard buys: the authored value's shape is not reinterpreted.
Arms, if anyone takes it
Not findings, recorded so the next sweep does not re-derive them
The same repo-wide sweep (?? {} / || {} on a .props / .properties member) turned up hits that are a different question and should not be filed as this one:
packages/plugin-detail/src/renderers/record-activity.tsx:105 — const bag = (schema.properties ?? {}) followed by keyed reads bag[key]. No spread, so nothing is enumerated; a degenerate value yields undefined per key.
packages/app-shell/src/views/metadata-admin/ResourceEditPage.tsx:1285 — keyed read props[key]?.title, no spread.
- The remaining
metadata-admin hits (SchemaForm.tsx, widgets.tsx, mergeServerFields.ts, json-schema-to-fields.ts, datasource/DatasourceResourcePage.tsx, previews/ToolPreview.tsx) read a JSON Schema properties map, not the SDUI config bag.
Related: #6783 (the five, converged), #6760 (the properties hoist), #6752 (the props half), #6761 (one predicate in packages/react), #6708 (the census). Found by PR #6789.
Measured while implementing #6783 (the five
readProps()copies inpackages/components/src/renderers/basic/), outside its fence — that card's file surface ispackages/components/src/renderers/basic/only — so filed separately. Observation-class in the same sense #6752, #6760 and #6783 were: nothing is known to depend on it today.The fact, measured on
107babef6packages/plugin-detail/src/renderers/record-alert.tsx:168:??only replacesnull/undefined, so a degeneratepropertiespasses straight into the object spread and is re-read as its own character indices —Object.keys({ ...'not-a-bag' })is["0","1","2","3","4","5","6","7","8"]. This is the identical hazard #6783 closed on the fiverenderers/basic/copies, on the same channel (the config bag a renderer reads), in a different expression: this one merges{ ...schema, ...schema.properties }rather than{ ...schema.props, ...schema.properties }, so it has nopropsalias leg but does put the node's own keys underneath.The
alias-precedence-cross-channel.test.tsxdocblock already names this site as the sixth member of the family, so it is known ground, not a new discovery — what is new is that #6783 has now converged the other five and left this one alone.Blast radius, honestly bounded
Same denominator as the sibling cards: #6708's census walked every JSON document, every
jsondoc fence and every TypeScript object literal and found zero authored nodes carrying a degenerate config bag. Latent shape, not a live failure.And the same measurement #6783 recorded about its own five applies here:
record-alertreads only named keys off the bag (props.title,props.body,props.severity,props.action?.label,props.dismissible, …) and never spreads it onto a DOM element, so today the indexed keys are computed and then dropped. Nothing rendered moves either way. What the guard would buy is what #6752 measured its own guard buys: the authored value's shape is not reinterpreted.Arms, if anyone takes it
readProps()copies object-spread a degenerate config bag — the third channel objectui#6752 / objectui#6760 left open #6783 that is a one-line import:isConfigBagis exported from@object-ui/react's package entry, and@object-ui/plugin-detailwould need to be checked for that dependency edge (@object-ui/componentsalready had it, which is what made finding(components): fivereadProps()copies object-spread a degenerate config bag — the third channel objectui#6752 / objectui#6760 left open #6783's fix a read rather than a sixth spelling).readPropsfinding(components): fivereadProps()copies object-spread a degenerate config bag — the third channel objectui#6752 / objectui#6760 left open #6783 added lives inpackages/components/src/renderers/basic/readProps.tsand is not exported from@object-ui/components— its barrel is side-effect imports only — so reusing the reader itself, rather than just the predicate, is a separate decision about where it should live.Not findings, recorded so the next sweep does not re-derive them
The same repo-wide sweep (
?? {}/|| {}on a.props/.propertiesmember) turned up hits that are a different question and should not be filed as this one:packages/plugin-detail/src/renderers/record-activity.tsx:105—const bag = (schema.properties ?? {})followed by keyed readsbag[key]. No spread, so nothing is enumerated; a degenerate value yieldsundefinedper key.packages/app-shell/src/views/metadata-admin/ResourceEditPage.tsx:1285— keyed readprops[key]?.title, no spread.metadata-adminhits (SchemaForm.tsx,widgets.tsx,mergeServerFields.ts,json-schema-to-fields.ts,datasource/DatasourceResourcePage.tsx,previews/ToolPreview.tsx) read a JSON Schemapropertiesmap, not the SDUI config bag.Related: #6783 (the five, converged), #6760 (the
propertieshoist), #6752 (thepropshalf), #6761 (one predicate inpackages/react), #6708 (the census). Found by PR #6789.