Measured while implementing objectui#6752, outside its fence (that card is scoped to the props bag and is explicitly forbidden from changing hoist behaviour), so filed separately. Observation-class in the same sense objectui#6752 was: nothing is known to depend on it today.
The fact, measured on b76ca6764
objectui#6752 fixed the props half of "a degenerate value must not have its shape reinterpreted by an object spread". The properties half is still open, and the site is NOT the evaluation memo — it is the hoist.
Rendered through the real SchemaRenderer with a probe that records the React props the element receives:
{ type: "test:deg", properties: "not-a-bag" }
-> React props named 0, 1, 2, 3, 4, 5, 6, 7, 8 (one per character)
{ type: "test:deg", properties: ["x", "y"] }
-> React props named 0, 1
Why the existing guard does not stop it
The evaluation memo guards properties with a real-object test, and its comment used to explain that guard by saying the value FEEDS the hoist. That explanation does not survive measurement. Ablating the guard to bare truthiness and re-rendering the same nodes leaves the indexed keys COMPLETELY UNCHANGED, because the hoist does its own enumeration:
if (newSchema.properties) {
const props = newSchema.properties;
for (const [k, v] of Object.entries(props)) { ... newSchema[k] = v; }
}
Object.entries("not-a-bag") yields the character indices whatever the memo did. The only thing the memo guard moves is whether schema.properties still holds the value the author wrote. That measurement is what selected the arm on objectui#6752, and the shape is pinned there in packages/react/src/__tests__/SchemaRenderer.degeneratePropsBag.test.tsx (the leg named "the properties channel is untouched — this card changed one bag"), so it is recorded as known and accepted rather than overlooked.
Blast radius, honestly bounded
Same denominator as objectui#6752: the objectui#6708 census walked every JSON document, every json doc fence and every TypeScript object literal and found zero authored nodes with a degenerate config bag. Latent shape, not a live failure. What makes it worth recording is that the two halves of one hazard are now fixed on one side and open on the other, and the open side is the one the spec spelling uses.
Arms, if anyone takes it
- guard the hoist the way the memo is guarded, so a degenerate
properties contributes no node keys; - or decide the hoist may enumerate whatever it is handed, and say so where the hoist is written.
Not picked here — objectui#6752 scoped hoist behaviour out explicitly.
Related: objectui#6752 (the props half, fixed), objectui#5123 (the two-bag precedence ruling), objectui#6708 (the census).
Measured while implementing objectui#6752, outside its fence (that card is scoped to the
propsbag and is explicitly forbidden from changing hoist behaviour), so filed separately. Observation-class in the same sense objectui#6752 was: nothing is known to depend on it today.The fact, measured on
b76ca6764objectui#6752 fixed the
propshalf of "a degenerate value must not have its shape reinterpreted by an object spread". Thepropertieshalf is still open, and the site is NOT the evaluation memo — it is the hoist.Rendered through the real
SchemaRendererwith a probe that records the React props the element receives:Why the existing guard does not stop it
The evaluation memo guards
propertieswith a real-object test, and its comment used to explain that guard by saying the value FEEDS the hoist. That explanation does not survive measurement. Ablating the guard to bare truthiness and re-rendering the same nodes leaves the indexed keys COMPLETELY UNCHANGED, because the hoist does its own enumeration:Object.entries("not-a-bag")yields the character indices whatever the memo did. The only thing the memo guard moves is whetherschema.propertiesstill holds the value the author wrote. That measurement is what selected the arm on objectui#6752, and the shape is pinned there inpackages/react/src/__tests__/SchemaRenderer.degeneratePropsBag.test.tsx(the leg named "thepropertieschannel is untouched — this card changed one bag"), so it is recorded as known and accepted rather than overlooked.Blast radius, honestly bounded
Same denominator as objectui#6752: the objectui#6708 census walked every JSON document, every
jsondoc fence and every TypeScript object literal and found zero authored nodes with a degenerate config bag. Latent shape, not a live failure. What makes it worth recording is that the two halves of one hazard are now fixed on one side and open on the other, and the open side is the one the spec spelling uses.Arms, if anyone takes it
propertiescontributes no node keys;Not picked here — objectui#6752 scoped hoist behaviour out explicitly.
Related: objectui#6752 (the
propshalf, fixed), objectui#5123 (the two-bag precedence ruling), objectui#6708 (the census).