You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Observation-class finding, recorded while implementing #4787 (PR #5573). Not fixed there — that card's file surface was grid.tsx alone, and packages/components is a large shared package.
The gap
packages/app-shell/src/__tests__/widget-dom-leak-sweep.test.tsx is the measurement gate for the #3291 / #4425 whitelist migration. Its target list is four plugin packages (plugin-charts, plugin-calendar, plugin-chatbot, plugin-dashboard). It does not sweep the packages/components renderers at all.
That is why ui:grid leaked unobserved: #4008's ratchet pins only className, and nothing else looked at the rendered attributes of a packages/components renderer. The leak was found by hand during #4011 / PR #4785, not by a gate.
The renderers still carrying it
The same bare-spread shape grid.tsx had — destructure data-obj-id / data-obj-type / style, spread the rest onto a host element — is present in at least these, all on origin/main at ac73c24b0:
Listed as candidates to measure, not asserted as defects — the shape is the same, but whether a given node's keys actually reach an element was verified only for grid. That verification is cheap and is exactly what the sweep does.
For reference, the measured grid reading before PR #5573, on a canary node — eight illegitimate attributes. (Tags are written with a space after < and before >, the same workaround #3291's body uses: GitHub's body sanitizer eats angle-bracket-delimited runs even inside fenced code blocks, and it emptied this block on the first revision of this issue.)
Why this is the pin worth having, rather than a warning-as-error pin
#4787 also asked whether React's unknown-attribute warning should be pinned as an error so the class stays closed. PR #5573 answers that in full; the short version is no, because such a pin is blind to most of the defect:
React does not warn for unknown all-lowercase attributes at all — it passes them through by design and stringifies object values. columns, gap, name, props produce zero output. Measured: 8 leaked attributes, 3 console.error calls.
The warning that does fire is the camelCase one, and React's own suggested remedy ("spell it lowercase") silences the warning while keeping the leak.
Vitest 4's reporter defaults to silent: 'passed-only', so console output from a passing test is discarded — the warning can only be seen in a run that is already red for another reason.
The technique that actually closes the class reads the DOM, not the console — which is what the sweep already does, and it catches lowercase and camelCase alike.
Suggested shape
Extend the existing sweep's target list to the registry-reachable renderers in packages/components, deleting rows from its ledger as each renderer converges on toDomProps — the same per-package migration pattern #4425 phase 2 already runs. The dependency-direction argument in that file's docblock still holds (app-shell may import components).
Sizing note: packages/components registers far more renderers than the four plugin packages did, so the first run should be expected to record a ledger, not to come back clean.
Observation-class finding, recorded while implementing #4787 (PR #5573). Not fixed there — that card's file surface was
grid.tsxalone, andpackages/componentsis a large shared package.The gap
packages/app-shell/src/__tests__/widget-dom-leak-sweep.test.tsxis the measurement gate for the #3291 / #4425 whitelist migration. Its target list is four plugin packages (plugin-charts,plugin-calendar,plugin-chatbot,plugin-dashboard). It does not sweep thepackages/componentsrenderers at all.That is why
ui:gridleaked unobserved: #4008's ratchet pins onlyclassName, and nothing else looked at the rendered attributes of apackages/componentsrenderer. The leak was found by hand during #4011 / PR #4785, not by a gate.The renderers still carrying it
The same bare-spread shape
grid.tsxhad — destructuredata-obj-id/data-obj-type/style, spread the rest onto a host element — is present in at least these, all onorigin/mainatac73c24b0:packages/components/src/renderers/layout/flex.tsx(flex.tsx:63-76)packages/components/src/renderers/layout/stack.tsx(stack.tsx:65-77)packages/components/src/renderers/layout/container.tsx(container.tsx:78-90)packages/components/src/renderers/basic/text.tsx(text.tsx:18-22)Listed as candidates to measure, not asserted as defects — the shape is the same, but whether a given node's keys actually reach an element was verified only for
grid. That verification is cheap and is exactly what the sweep does.For reference, the measured
gridreading before PR #5573, on a canary node — eight illegitimate attributes. (Tags are written with a space after<and before>, the same workaround #3291's body uses: GitHub's body sanitizer eats angle-bracket-delimited runs even inside fenced code blocks, and it emptied this block on the first revision of this issue.)Why this is the pin worth having, rather than a warning-as-error pin
#4787 also asked whether React's unknown-attribute warning should be pinned as an error so the class stays closed. PR #5573 answers that in full; the short version is no, because such a pin is blind to most of the defect:
columns,gap,name,propsproduce zero output. Measured: 8 leaked attributes, 3console.errorcalls.silent: 'passed-only', so console output from a passing test is discarded — the warning can only be seen in a run that is already red for another reason.console.errorcase written against a shared canary passed against code leaking ten attributes, because earlier cases consumed the latch.The technique that actually closes the class reads the DOM, not the console — which is what the sweep already does, and it catches lowercase and camelCase alike.
Suggested shape
Extend the existing sweep's target list to the registry-reachable renderers in
packages/components, deleting rows from its ledger as each renderer converges ontoDomProps— the same per-package migration pattern #4425 phase 2 already runs. The dependency-direction argument in that file's docblock still holds (app-shellmay importcomponents).Sizing note:
packages/componentsregisters far more renderers than the four plugin packages did, so the first run should be expected to record a ledger, not to come back clean.References
gridfix and the full React-warning answertoDomPropswhitelist stops atpackages/fields— SDUI widgets elsewhere still close their DOM leak by hand, if at all #4425 — the tracking card for the whitelist migration (this is filed as its sub-issue)schema="[object Object]"出现在真实输入框上 #3291 / PR fix(fields): close the widget DOM prop leak with a whitelisttoDomProps#3313 — where the whitelist discipline was establishedpackages/core/src/utils/dom-props.ts— the shared executorpackages/app-shell/src/__tests__/widget-dom-leak-sweep.test.tsx— the gate to extendGenerated by Claude Code