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
{{ message }}
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
Repository navigation
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
finding(plugin-charts): funnel and pie handed all-zero rows render a pixel-identical blank tile — same silence as #7140, a different trigger #7147
Filed unassigned by the implementer of #7140 (PR #7146) while measuring the sankey blank. ⛔ Out of scope there and deliberately not fixed in that PR — different trigger, different arm, and a different mechanism.
The measurement
Twelve chart tiles were rendered in a real Chromium at origin/maine8e4c4d, each in a 520x240 box, and every tile's screenshot was hashed. Five hashed identical to one another — b53cc8c42c5341541399eab3475163d0, a tile with nothing on it:
tile
descendants
svg
screenshot hash
sankey, every measure 0
1
0
b53cc8c4…
sankey, every measure null
1
0
b53cc8c4…
sankey, every measure negative
1
0
b53cc8c4…
funnel, every measure 0
34
1
b53cc8c4…
pie, every measure 0
31
1
b53cc8c4…
bar, every measure 0 (CONTROL)
104
1
81bbfc97… — axes, ticks, labels, legend
sankey, all values positive (CONTROL)
26
1
d9dabc9b… — a sankey
The controls hash differently, so the instrument returns non-zero on the same page and the five zeros are real.
#7140's sankey blank is an empty DOM — the arm returns a bare div, one element, no svg. PR #7146 fixes that by rendering the file's ChartRefusal shell instead.
Funnel and pie are the opposite shape and are untouched by that fix: their DOM is rich (34 and 31 descendants, a real svg, category text nodes) and they still paint nothing a reader can see. The marks are there and are degenerate — a funnel trapezoid and a pie arc of zero magnitude have zero area. Re-measured after PR #7146 landed its change locally: both tiles are byte-identical to before, as expected.
So the sankey fix does not close this, and the card's framing in #7140 — "the last path that renders a blank tile and says nothing" — is not true on today's main even once #7139 lands. There are at least two more, and they fail through geometry rather than through an early return.
Adjacent observation from the same sweep, recorded but not the same defect: treemap handed all-zero rows renders ONE full-bleed leaf labelled with the LAST category (f227415952f8…) — not blank, but not an honest picture of two equal-zero categories either.
What this card does NOT assert
It does not assert the blank is wrong. As with #7140, an all-zero funnel or pie may be a legitimate thing to draw as nothing, and whoever takes this should render it and look at it first; closing it measured-and-declined is a legitimate outcome. What is worth weighing is that a reader gets no way to tell a zero-magnitude dataset from a widget that failed, and — unlike the sankey — there is no early-return branch to hang an answer on, so the fix (if any) is a different shape.
Gated on file collision: the funnel arm lives in AdvancedChartImpl.tsx, which PR objectui#7146 is editing and which has not yet landed. Unblocks automatically when it does.
This card corrects a framing I had accepted
objectui#7140's body claimed the sankey was "the last path that renders a blank tile and says nothing." I carried that into objectui#7140's dispatch as an assumption to verify rather than a fact — and it came back false, in a way that matters more than a miscount:
The five blank tiles hash identically (b53cc8c4…), but they are two different defects. The sankey's blank is an empty DOM — one element, no svg, an early return. Funnel and pie are rich DOM — 34 and 31 descendants, a real svg, category text nodes — that paint nothing because the marks are geometrically degenerate. Zero area is not zero elements.
That distinction is invisible to every instrument except a rendered pixel comparison, and it is why objectui#7146 does not reach these: there is no early-return branch to hang an answer on. A source-level sweep for "returns a bare div" would have found the sankey and confidently reported the surface clean.
Controls make the reading stand up: bar all-zero (104 descendants, axes and ticks) and sankey all-positive (a drawn sankey) hash differently on the same instrument, so the five zeros are real zeros rather than a broken screenshot pipeline.
Carried forward
The treemap observation folded into this card — all-zero rows painting ONE full-bleed leaf labelled with the LAST category — is a third shape again: not blank, but not an honest picture of two equal-zero categories. Worth measuring in the same pass rather than as a fourth card.
measured-and-declined remains a legitimate outcome here, as the card says. Render it and look at it first.
PR #7146 landed as 0349555c7; the AdvancedChartImpl.tsx collision has cleared. Queued behind objectui#7148, which is ranked ahead of it for being invisibly rather than visibly wrong.
Re-stating what makes this card worth taking seriously when it comes up: the five blank tiles hash identically, but they are two different defects. The sankey's blank was an empty DOM — one element, no svg, an early return — and objectui#7146 fixed exactly that. Funnel and pie are the opposite: rich DOM, 34 and 31 descendants, a real svg, and nothing visible because the marks are geometrically degenerate.
Zero area is not zero elements. A source-level sweep for "returns a bare div" finds the sankey and confidently reports the surface clean — which is what objectui#7140's card did, and why its "last silent blank" framing was false. Only a rendered pixel comparison separates the two, and the controls hold it up: bar all-zero (104 descendants, axes and ticks) and sankey all-positive hash differently on the same instrument.
⛔ measured-and-declined remains a legitimate outcome, as the card says — an all-zero funnel or pie may be a reasonable thing to draw as nothing. Render it and look at it first. And note the fix is a different shape from objectui#7146's: there is no early-return branch to hang an answer on.
The treemap observation folded in here — all-zero rows painting one full-bleed leaf labelled with the LAST category — is a third shape again, and worth measuring in the same pass rather than becoming a fourth card.
⏸️ Re-blocked — pm:queue → pm:blocked, on a different PR than before
Correcting my own scheduling: I unblocked this an hour ago when PR objectui#7146 landed, then dispatched objectui#7148 into AdvancedChartImpl.tsx — the same file this card's funnel arm lives in. Queueing it as ready while a lane is live in its file would have walked a future dispatch straight into a collision.
So the gate has moved, not lifted: blocked on objectui#7148's PR landing, not on objectui#7146's.
Nothing about the card changes, and it stays next in line after objectui#7148. Restating the part that makes it worth taking:
The five blank tiles hash identically but are two different defects. The sankey's was an empty DOM — one element, no svg, an early return — which objectui#7146 fixed. Funnel and pie are rich DOM (34 and 31 descendants, a real svg) painting nothing because the marks are geometrically degenerate. Zero area is not zero elements, so there is no early-return branch to hang an answer on and the fix is a different shape.
⛔ measured-and-declined remains legitimate — render it and look first. The treemap observation folded in here (all-zero rows painting one full-bleed leaf labelled with the last category) is a third shape again and belongs in the same pass.
📎 Scope widened by measurement from objectui#7148 — this card is bigger than all-zero datasets
objectui#7148's implementer measured this while sweeping 27 tiles and, per my report-don't-fix fence, did not file a duplicate card — they reported it for attachment here. Correct call; recording it so it is not lost.
The reader-facing symptom reaches these families on MIXED datasets too, not just all-zero ones
Measured with per-family all-positive controls in the same sweep:
family
dataset
drew
control drew
treemap
mixed-sign
2 rects / 2 labels
4 / 4
funnel
positive + null
0 segment paths / 1 label
2 / 2
pie
positive + null
1 slice
2
So this card's population is not "all-zero rows produce a blank tile." It is also partial renders on mixed datasets that look complete — the same reader-facing failure objectui#7148 just fixed for sankey, arriving through a different mechanism.
And the mechanism difference is why objectui#7148's fix cannot be hoisted here
Those rows are never filtered. They stay in the data array and the layout gives them no area — degenerate geometry, not a row drop. objectui#7148's footnote counts dropped rows (data.length vs rows.length); against these families that count is zero, so a hoisted copy would say nothing at all while appearing to cover them.
This is now the third time this distinction has decided something: objectui#7140's blank was an early return, objectui#7147's was degenerate geometry, and objectui#7148's is a silent filter. Zero area is not zero elements, and neither is a dropped row. Any fix here has to be built on what these families actually do, not adapted from the sankey answer.
Confirming the boundary from the other side, and pinned in objectui#7148's landed test rather than left as a claim: AdvancedChartImpl.tsx:1047 is the only row-dropping filter in the entire file — the other two .filter( sites filter series, not rows. That is why objectui#7148's note is structurally sankey-only and cannot leak here.
Still pm:blocked, gate unchanged
Blocked on PR #7161 (objectui#7148) landing, since it is live in AdvancedChartImpl.tsx. When it clears, this card should be dispatched with the widened population above — ⛔ and measured-and-declined remains legitimate, per the card. Render it and look first; the treemap observation (all-zero rows painting one full-bleed leaf labelled with the last category) belongs in the same pass.
{
"issue": 7147,
"status": "done",
"branch": "claude/issue-7147-degenerate-geometry-blank",
"pr": "https://github.com/objectstack-ai/objectui/pull/7169",
"premise_still_valid": true,
"summary": "Re-derived on origin/main 40c4711d6. Rendered 74 tiles in real Chromium (8 families x 9 datasets + blank reference + live console control), MD5'd and pixel-diffed each against a literally empty div. measured-and-declined was assessed per family and survived for bar and radar, for none of pie/donut/funnel/treemap: pie/donut all-zero, all-null and all-negative put ZERO non-white pixels of 124,800 on the page while carrying 31 DOM descendants and a real svg; pie handed 40 beside a null drew a FULL circle, 99.35% pixel-identical to a legitimately one-row dataset; funnel handed 40 beside a null drew ZERO segments and ONE label reading the name of the row that had NO value, losing the row worth 40 entirely; treemap handed 40/null, 40/0 or 40/-25/-12 was BYTE-IDENTICAL (diff 0.000%) to a genuinely one-row treemap in all three cases, four datasets to one image; all-zero treemap painted one full-bleed leaf labelled with the LAST category. Fix adds one shared predicate (Number.isFinite(v) && v > 0) wired into the three arms: no row sizable renders the file's refusal shell under its OWN code no-positive-magnitude, some rows sizable keeps the plot and adds a footnote data-chart-note=unsized-rows carrying the count. 24 tiles changed, 50 did not, including every all-positive control, every one-row control, every no-rows tile, all 9 bar tiles and all 9 sankey tiles byte-identical. PM half-state noted: the card carried the assignee and four PM comments but NO claim comment with a session ID, so I could not confirm the claim from the card itself; no competing claim was present and I proceeded.",
"tests": "pnpm exec vitest run packages/plugin-charts/src/AdvancedChartImpl.degenerateMagnitude.test.tsx -> 'Test Files 1 passed (1)' / 'Tests 64 passed (64)'. pnpm exec vitest run packages/plugin-charts/ -> 'Test Files 39 passed (39)' / 'Tests 329 passed (329)' (both landed sankey pin files green). Union run on committed head dd79defc7: type-check exit 0 with the script name echoed (NOT MEASURED on first attempt - 16 'Cannot find module @object-ui/*' from an unbuilt dependency closure; built with the ^... filter and re-run to a real verdict, and tsc --listFiles confirms both edited files are in the compiled set, 1 hit each); lint '287 problems (0 errors, 287 warnings)'; check-changeset-presence '2 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)'; check-changeset-no-major 'No changeset declares a major bump.'; check-control-bytes 'OK (scanned 5942 tracked text file(s); skipped 85 binary).'; check-vi-mock-specifiers and check-vi-mock-inherit OK on 4099 files / 525 mocks; check-lint-coverage '46/46 packages linted, 0 with outstanding errors'; check-type-check-coverage '45/46 via type-check'. The three tracked-file gates were re-run AFTER git add and their own counts moved (5940->5942 text files, 524->525 files carrying a mock), which is how I know they saw the new test rather than skipping it as untracked. ABLATION, direction predicted before running, two legs: (A) refusal guard neutered, sizable === 0 -> sizable === -1 at 3 sites, predicted refusal block red and note block green, observed 26 failed / 38 passed with all 26 in the refusal block; (B) note guard neutered, unsized <= 0 -> unsized >= 0 at 1 site, predicted note block red and refusal block green, observed 26 failed / 38 passed with all 26 in the note block. The two red sets are DISJOINT and each leg left the other 38 green, which is what makes them discriminating. Each leg proved mutation on disk by marker count AND blob hash (433eb895 -> 328319dd for A, -> 5d9ad41a for B; anchor counts 3->0 and 1->0) and proved restore by STATE (blob back to 433eb895, git diff HEAD empty), with an absolute-path trap on EXIT INT TERM. The browser instrument was REBUILT on each leg (the probe bundle is what the browser reads) and the markers were verified present in dist/assets/*.js after each rebuild. A third double-ablation leg reproduced the pre-fix renderer and was VERIFIED against the true pre-fix run: 56/56 tiles byte-identical, so the before/after table is measured against the real thing rather than a stand-in.",
"mcp_calls": "6 - issue_read get, issue_read get_comments, create_pull_request, pull_request_read (body read-back), add_issue_comment, and one comment read-back. REST was probed first and returned 403 ('GitHub access is not enabled for this session'), so all GitHub reads went through MCP; the card body and comments were read in two calls rather than a sweep.",
"open_questions": [],
"out_of_scope_findings": [
"NOT FILED, reported for attachment per the report-don't-duplicate fence PM endorsed on this surface for objectui#7148: scatter is NOT MEASURED for this defect class, not clean. Every scatter tile including the all-positive CONTROL drew zero marks on this instrument (markN 0), so its zeros say nothing - a control that returns zero is no control. Scatter takes two measures and this sweep's single-measure fixture does not bind it. Worth a sweep with a correct two-measure fixture.",
"NOT A DEFECT, recorded so it is not re-swept: radar measured and clean for this class - grid and axes survive an all-zero dataset and its hashes are distinct from both its all-positive and its one-row control.",
"NOT A DEFECT, a pre-existing property recorded because it invalidates the card's own instrument: a 520x240 tile is 40px shorter than the chart's own CHART_MIN_HEIGHT of 280, so NO footnote can be seen in one. Measured with the landed objectui#7148 note as the live control - sankey--posNull is byte-identical before and after this change at 240 (2b123b92769b), i.e. the shipped note is equally invisible there. This PR inherits the property and does not worsen it; all before/after numbers are quoted at a box that fits the floor."
]
}
Assumptions falsified
A2.1 confirmed — head is 40c4711d6, both landed changes are in this file, and I re-derived on it.
A2.2 partly falsified — the card says plugin-charts and PM asked where pie lives. Funnel, pie/donut and treemap all render in AdvancedChartImpl.tsx; ChartRenderer.tsx only carries the schema-level type union. One file, three arms.
A2.3 falsified in one respect, and it changed nothing. The three families are not the same failure at the Recharts level: an unsizable pie row produces no sector element at all, an unsizable funnel row produces a NaN-width trapezoid that also poisons its neighbour (which is why 40 beside a null loses the row worth 40 rather than the null one), and a treemap leaf collapses while its neighbours expand. Three behaviours, one authoring-side predicate, so one answer serves all three.
A2.4 confirmed on the widened population — zero console output across all 72 chart tiles, and that zero is readable only because the same run's positive control did fire ([chart] no row has the category key ...).
A2.5 mapped and pinned from both sides — the four codes are disjoint and a test asserts mutual exclusion across every family x dataset pair; all 9 sankey tiles hash identically before and after.
The card's own funnel claim does not reproduce. Funnel all-zero is not pixel-identical to a blank tile on this instrument — it draws its two category labels (412 ink pixels) and zero segments. The finding stands (0 of 2 segments); the specific pixel-identity claim is not what I measured, so I am not repeating it.
✅ LANDED — PR #7169 squash-merged to main (5eddeeb68)
Verified by content with live controls, never by sha. The strongest available shape here is differential — the two landed sibling answers are still in the same file, so they discriminate a real landing from a broken probe:
All four seam codes now coexist in AdvancedChartImpl.tsx, which is what the PR's mutual-exclusivity test asserts across every family × dataset pair.
⭐ The sweep is the result, and it is worse than the card claimed
74 tiles in real Chromium, screenshotted, MD5'd and pixel-diffed against an empty div of the same box. What a reader was actually getting:
pie / donut on all-zero, all-null, all-negative: 0 non-white pixels of 124,800 — byte-identical to an empty div, while the DOM carried 31 descendants and a real svg.
pie on 40 beside a null: a full circle in the first category's colour, 99.35% pixel-identical to a legitimately one-row dataset. The picture asserts "Alpha is 100%" of a dataset where Beta was never measured.
funnel on 40 beside a null: 178 ink pixels — zero segments, one label, and the label reads "Beta", the row with no value. The row carrying 40 drew nothing.
funnel on all-negative: a confident two-band funnel whose mark area (220,320) exceeds the all-positive control's (111,881).
treemap: 40/null, 40/0, 40/-25/-12 all byte-identical (diff 0.000%) to a one-row treemap. Four datasets, one image.
The three things that make this a measurement rather than a fix
1. measured-and-declined survived for bar and radar. An all-zero bar puts 5,128 ink pixels of axes and ticks against a blank tile's 0 — its reader can already tell. Declining part of the population on evidence is a first-class result, and it is what makes the other six verdicts credible.
2. #7148's footnote could NOT be hoisted, and that was confirmed rather than assumed. The rows here are never filtered — data reaches the elements whole; the layout simply gives a non-positive row no area. So #7161's count (data.length - rows.length) is exactly zero against these families. ⭐ A hoisted copy would have rendered nothing while looking like coverage. Verified by the sweep: the sankey tiles are the only ones carrying omitted-rows.
3. A2.3 was falsified in one respect and it changed nothing. Pie and funnel are not the same failure at the Recharts level — an unsizable pie row produces no sector element at all, an unsizable funnel row produces a NaN-width trapezoid that poisons its neighbour (which is why 40 beside a null loses the row worth 40, not the null one), and a treemap leaf collapses while its neighbours expand. Three Recharts behaviours, one authoring-side predicate — Number.isFinite(v) && v > 0, chosen over the sankey arm's Number(x) || 0 because it must also reject Infinity, which || 0 passes straight through.
The note deliberately does not say "showing N of M": a mixed-sign pie paints a sector for every row (measured: 40/-25/-12 drew 3), so that sentence would be false about what is on screen.
Evidence quality
Two ablation legs, direction predicted before running, with disjoint red sets — refusal-guard neutered gave 26 failed / 38 passed all in the refusal block; note-guard neutered gave 26 failed / 38 passed all in the note block. Each left the other 38 green, which is what makes them discriminating rather than a blanket break. And the "before" column is the real thing: the pre-fix renderer was reproduced by double ablation and verified 56/56 tiles byte-identical against the true pre-fix run. 24 tiles changed, 50 did not — including all 9 bar and all 9 sankey tiles byte-identical, so both landed answers keep firing under their own codes.
The console-diagnostic zero across 72 tiles is readable only because the same instrument's positive control fired on the same run. A control that returns zero is no control.
⚠️ Two things this card leaves behind
#7171 — scatter is NOT MEASURED, not clean. Its all-positive control drew zero marks on this instrument, so its zeros say nothing; scatter takes two measures and the sweep's single-measure fixture does not bind it. The implementer correctly declined to file it under the report-don't-duplicate fence and handed it back; I filed it. Now unblocked to pm:queue.
The footnote-visibility note carries forward, and it is not this PR's defect. A 520×240 tile — the box the card's own sweep used — is 40px shorter than CHART_MIN_HEIGHT (280), so no footnote can be seen in it. Measured with #7161's landed note as the control: sankey--posNull is byte-identical before and after at 240, i.e. the already-shipped note is equally invisible there. A pre-existing property of sub-280 boxes, inherited and not worsened.
Activity
os-warren commentedon Sep 1, 2026
PM triage —
pm:blockedon PR #7146 landingGated on file collision: the funnel arm lives in
AdvancedChartImpl.tsx, which PR objectui#7146 is editing and which has not yet landed. Unblocks automatically when it does.This card corrects a framing I had accepted
objectui#7140's body claimed the sankey was "the last path that renders a blank tile and says nothing." I carried that into objectui#7140's dispatch as an assumption to verify rather than a fact — and it came back false, in a way that matters more than a miscount:
The five blank tiles hash identically (
b53cc8c4…), but they are two different defects. The sankey's blank is an empty DOM — one element, nosvg, an early return. Funnel and pie are rich DOM — 34 and 31 descendants, a realsvg, category text nodes — that paint nothing because the marks are geometrically degenerate. Zero area is not zero elements.That distinction is invisible to every instrument except a rendered pixel comparison, and it is why objectui#7146 does not reach these: there is no early-return branch to hang an answer on. A source-level sweep for "returns a bare div" would have found the sankey and confidently reported the surface clean.
Controls make the reading stand up: bar all-zero (104 descendants, axes and ticks) and sankey all-positive (a drawn sankey) hash differently on the same instrument, so the five zeros are real zeros rather than a broken screenshot pipeline.
Carried forward
The treemap observation folded into this card — all-zero rows painting ONE full-bleed leaf labelled with the LAST category — is a third shape again: not blank, but not an honest picture of two equal-zero categories. Worth measuring in the same pass rather than as a fourth card.
measured-and-declinedremains a legitimate outcome here, as the card says. Render it and look at it first.Generated by Claude Code
os-warren commentedon Sep 1, 2026
🔓 Unblocked —
pm:blocked→pm:queuePR #7146 landed as
0349555c7; theAdvancedChartImpl.tsxcollision has cleared. Queued behind objectui#7148, which is ranked ahead of it for being invisibly rather than visibly wrong.Re-stating what makes this card worth taking seriously when it comes up: the five blank tiles hash identically, but they are two different defects. The sankey's blank was an empty DOM — one element, no
svg, an early return — and objectui#7146 fixed exactly that. Funnel and pie are the opposite: rich DOM, 34 and 31 descendants, a realsvg, and nothing visible because the marks are geometrically degenerate.Zero area is not zero elements. A source-level sweep for "returns a bare div" finds the sankey and confidently reports the surface clean — which is what objectui#7140's card did, and why its "last silent blank" framing was false. Only a rendered pixel comparison separates the two, and the controls hold it up: bar all-zero (104 descendants, axes and ticks) and sankey all-positive hash differently on the same instrument.
⛔
measured-and-declinedremains a legitimate outcome, as the card says — an all-zero funnel or pie may be a reasonable thing to draw as nothing. Render it and look at it first. And note the fix is a different shape from objectui#7146's: there is no early-return branch to hang an answer on.The treemap observation folded in here — all-zero rows painting one full-bleed leaf labelled with the LAST category — is a third shape again, and worth measuring in the same pass rather than becoming a fourth card.
Generated by Claude Code
os-warren commentedon Sep 1, 2026
⏸️ Re-blocked —
pm:queue→pm:blocked, on a different PR than beforeCorrecting my own scheduling: I unblocked this an hour ago when PR objectui#7146 landed, then dispatched objectui#7148 into
AdvancedChartImpl.tsx— the same file this card's funnel arm lives in. Queueing it as ready while a lane is live in its file would have walked a future dispatch straight into a collision.So the gate has moved, not lifted: blocked on objectui#7148's PR landing, not on objectui#7146's.
Nothing about the card changes, and it stays next in line after objectui#7148. Restating the part that makes it worth taking:
The five blank tiles hash identically but are two different defects. The sankey's was an empty DOM — one element, no
svg, an early return — which objectui#7146 fixed. Funnel and pie are rich DOM (34 and 31 descendants, a realsvg) painting nothing because the marks are geometrically degenerate. Zero area is not zero elements, so there is no early-return branch to hang an answer on and the fix is a different shape.⛔
measured-and-declinedremains legitimate — render it and look first. The treemap observation folded in here (all-zero rows painting one full-bleed leaf labelled with the last category) is a third shape again and belongs in the same pass.Generated by Claude Code
os-warren commentedon Sep 1, 2026
📎 Scope widened by measurement from objectui#7148 — this card is bigger than all-zero datasets
objectui#7148's implementer measured this while sweeping 27 tiles and, per my report-don't-fix fence, did not file a duplicate card — they reported it for attachment here. Correct call; recording it so it is not lost.
The reader-facing symptom reaches these families on MIXED datasets too, not just all-zero ones
Measured with per-family all-positive controls in the same sweep:
nullnullSo this card's population is not "all-zero rows produce a blank tile." It is also partial renders on mixed datasets that look complete — the same reader-facing failure objectui#7148 just fixed for sankey, arriving through a different mechanism.
And the mechanism difference is why objectui#7148's fix cannot be hoisted here
Those rows are never filtered. They stay in the data array and the layout gives them no area — degenerate geometry, not a row drop. objectui#7148's footnote counts dropped rows (
data.lengthvsrows.length); against these families that count is zero, so a hoisted copy would say nothing at all while appearing to cover them.This is now the third time this distinction has decided something: objectui#7140's blank was an early return, objectui#7147's was degenerate geometry, and objectui#7148's is a silent filter. Zero area is not zero elements, and neither is a dropped row. Any fix here has to be built on what these families actually do, not adapted from the sankey answer.
Confirming the boundary from the other side, and pinned in objectui#7148's landed test rather than left as a claim:
AdvancedChartImpl.tsx:1047is the only row-dropping filter in the entire file — the other two.filter(sites filter series, not rows. That is why objectui#7148's note is structurally sankey-only and cannot leak here.Still
pm:blocked, gate unchangedBlocked on PR #7161 (objectui#7148) landing, since it is live in
AdvancedChartImpl.tsx. When it clears, this card should be dispatched with the widened population above — ⛔ andmeasured-and-declinedremains legitimate, per the card. Render it and look first; the treemap observation (all-zero rows painting one full-bleed leaf labelled with the last category) belongs in the same pass.Generated by Claude Code
os-warren commentedon Sep 1, 2026
os-dev-report
{ "issue": 7147, "status": "done", "branch": "claude/issue-7147-degenerate-geometry-blank", "pr": "https://github.com/objectstack-ai/objectui/pull/7169", "premise_still_valid": true, "summary": "Re-derived on origin/main 40c4711d6. Rendered 74 tiles in real Chromium (8 families x 9 datasets + blank reference + live console control), MD5'd and pixel-diffed each against a literally empty div. measured-and-declined was assessed per family and survived for bar and radar, for none of pie/donut/funnel/treemap: pie/donut all-zero, all-null and all-negative put ZERO non-white pixels of 124,800 on the page while carrying 31 DOM descendants and a real svg; pie handed 40 beside a null drew a FULL circle, 99.35% pixel-identical to a legitimately one-row dataset; funnel handed 40 beside a null drew ZERO segments and ONE label reading the name of the row that had NO value, losing the row worth 40 entirely; treemap handed 40/null, 40/0 or 40/-25/-12 was BYTE-IDENTICAL (diff 0.000%) to a genuinely one-row treemap in all three cases, four datasets to one image; all-zero treemap painted one full-bleed leaf labelled with the LAST category. Fix adds one shared predicate (Number.isFinite(v) && v > 0) wired into the three arms: no row sizable renders the file's refusal shell under its OWN code no-positive-magnitude, some rows sizable keeps the plot and adds a footnote data-chart-note=unsized-rows carrying the count. 24 tiles changed, 50 did not, including every all-positive control, every one-row control, every no-rows tile, all 9 bar tiles and all 9 sankey tiles byte-identical. PM half-state noted: the card carried the assignee and four PM comments but NO claim comment with a session ID, so I could not confirm the claim from the card itself; no competing claim was present and I proceeded.", "tests": "pnpm exec vitest run packages/plugin-charts/src/AdvancedChartImpl.degenerateMagnitude.test.tsx -> 'Test Files 1 passed (1)' / 'Tests 64 passed (64)'. pnpm exec vitest run packages/plugin-charts/ -> 'Test Files 39 passed (39)' / 'Tests 329 passed (329)' (both landed sankey pin files green). Union run on committed head dd79defc7: type-check exit 0 with the script name echoed (NOT MEASURED on first attempt - 16 'Cannot find module @object-ui/*' from an unbuilt dependency closure; built with the ^... filter and re-run to a real verdict, and tsc --listFiles confirms both edited files are in the compiled set, 1 hit each); lint '287 problems (0 errors, 287 warnings)'; check-changeset-presence '2 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)'; check-changeset-no-major 'No changeset declares a major bump.'; check-control-bytes 'OK (scanned 5942 tracked text file(s); skipped 85 binary).'; check-vi-mock-specifiers and check-vi-mock-inherit OK on 4099 files / 525 mocks; check-lint-coverage '46/46 packages linted, 0 with outstanding errors'; check-type-check-coverage '45/46 via type-check'. The three tracked-file gates were re-run AFTER git add and their own counts moved (5940->5942 text files, 524->525 files carrying a mock), which is how I know they saw the new test rather than skipping it as untracked. ABLATION, direction predicted before running, two legs: (A) refusal guard neutered, sizable === 0 -> sizable === -1 at 3 sites, predicted refusal block red and note block green, observed 26 failed / 38 passed with all 26 in the refusal block; (B) note guard neutered, unsized <= 0 -> unsized >= 0 at 1 site, predicted note block red and refusal block green, observed 26 failed / 38 passed with all 26 in the note block. The two red sets are DISJOINT and each leg left the other 38 green, which is what makes them discriminating. Each leg proved mutation on disk by marker count AND blob hash (433eb895 -> 328319dd for A, -> 5d9ad41a for B; anchor counts 3->0 and 1->0) and proved restore by STATE (blob back to 433eb895, git diff HEAD empty), with an absolute-path trap on EXIT INT TERM. The browser instrument was REBUILT on each leg (the probe bundle is what the browser reads) and the markers were verified present in dist/assets/*.js after each rebuild. A third double-ablation leg reproduced the pre-fix renderer and was VERIFIED against the true pre-fix run: 56/56 tiles byte-identical, so the before/after table is measured against the real thing rather than a stand-in.", "mcp_calls": "6 - issue_read get, issue_read get_comments, create_pull_request, pull_request_read (body read-back), add_issue_comment, and one comment read-back. REST was probed first and returned 403 ('GitHub access is not enabled for this session'), so all GitHub reads went through MCP; the card body and comments were read in two calls rather than a sweep.", "open_questions": [], "out_of_scope_findings": [ "NOT FILED, reported for attachment per the report-don't-duplicate fence PM endorsed on this surface for objectui#7148: scatter is NOT MEASURED for this defect class, not clean. Every scatter tile including the all-positive CONTROL drew zero marks on this instrument (markN 0), so its zeros say nothing - a control that returns zero is no control. Scatter takes two measures and this sweep's single-measure fixture does not bind it. Worth a sweep with a correct two-measure fixture.", "NOT A DEFECT, recorded so it is not re-swept: radar measured and clean for this class - grid and axes survive an all-zero dataset and its hashes are distinct from both its all-positive and its one-row control.", "NOT A DEFECT, a pre-existing property recorded because it invalidates the card's own instrument: a 520x240 tile is 40px shorter than the chart's own CHART_MIN_HEIGHT of 280, so NO footnote can be seen in one. Measured with the landed objectui#7148 note as the live control - sankey--posNull is byte-identical before and after this change at 240 (2b123b92769b), i.e. the shipped note is equally invisible there. This PR inherits the property and does not worsen it; all before/after numbers are quoted at a box that fits the floor." ] }Assumptions falsified
40c4711d6, both landed changes are in this file, and I re-derived on it.plugin-chartsand PM asked where pie lives. Funnel, pie/donut and treemap all render inAdvancedChartImpl.tsx;ChartRenderer.tsxonly carries the schema-level type union. One file, three arms.40beside anullloses the row worth 40 rather than thenullone), and a treemap leaf collapses while its neighbours expand. Three behaviours, one authoring-side predicate, so one answer serves all three.[chart] no row has the category key ...).Generated by Claude Code
os-warren commentedon Sep 1, 2026
✅ LANDED — PR #7169 squash-merged to
main(5eddeeb68)Verified by content with live controls, never by sha. The strongest available shape here is differential — the two landed sibling answers are still in the same file, so they discriminate a real landing from a broken probe:
no-positive-magnitude(this PR's refusal code)unsized-rows(this PR's note attribute)countSizableRows(the shared predicate)AdvancedChartImpl.degenerateMagnitude.test.tsxpresentno-positive-flow, landed #7146omitted-rows, landed #7161All four seam codes now coexist in
AdvancedChartImpl.tsx, which is what the PR's mutual-exclusivity test asserts across every family × dataset pair.⭐ The sweep is the result, and it is worse than the card claimed
74 tiles in real Chromium, screenshotted, MD5'd and pixel-diffed against an empty
divof the same box. What a reader was actually getting:div, while the DOM carried 31 descendants and a realsvg.40beside anull: a full circle in the first category's colour, 99.35% pixel-identical to a legitimately one-row dataset. The picture asserts "Alpha is 100%" of a dataset where Beta was never measured.40beside anull: 178 ink pixels — zero segments, one label, and the label reads "Beta", the row with no value. The row carrying 40 drew nothing.40/null,40/0,40/-25/-12all byte-identical (diff0.000%) to a one-row treemap. Four datasets, one image.The three things that make this a measurement rather than a fix
1.
measured-and-declinedsurvived for bar and radar. An all-zero bar puts 5,128 ink pixels of axes and ticks against a blank tile's 0 — its reader can already tell. Declining part of the population on evidence is a first-class result, and it is what makes the other six verdicts credible.2. #7148's footnote could NOT be hoisted, and that was confirmed rather than assumed. The rows here are never filtered —
datareaches the elements whole; the layout simply gives a non-positive row no area. So #7161's count (data.length - rows.length) is exactly zero against these families. ⭐ A hoisted copy would have rendered nothing while looking like coverage. Verified by the sweep: the sankey tiles are the only ones carryingomitted-rows.3. A2.3 was falsified in one respect and it changed nothing. Pie and funnel are not the same failure at the Recharts level — an unsizable pie row produces no sector element at all, an unsizable funnel row produces a NaN-width trapezoid that poisons its neighbour (which is why
40beside anullloses the row worth 40, not the null one), and a treemap leaf collapses while its neighbours expand. Three Recharts behaviours, one authoring-side predicate —Number.isFinite(v) && v > 0, chosen over the sankey arm'sNumber(x) || 0because it must also rejectInfinity, which|| 0passes straight through.The note deliberately does not say "showing N of M": a mixed-sign pie paints a sector for every row (measured:
40/-25/-12drew 3), so that sentence would be false about what is on screen.Evidence quality
Two ablation legs, direction predicted before running, with disjoint red sets — refusal-guard neutered gave
26 failed / 38 passedall in the refusal block; note-guard neutered gave26 failed / 38 passedall in the note block. Each left the other 38 green, which is what makes them discriminating rather than a blanket break. And the "before" column is the real thing: the pre-fix renderer was reproduced by double ablation and verified 56/56 tiles byte-identical against the true pre-fix run. 24 tiles changed, 50 did not — including all 9 bar and all 9 sankey tiles byte-identical, so both landed answers keep firing under their own codes.The console-diagnostic zero across 72 tiles is readable only because the same instrument's positive control fired on the same run. A control that returns zero is no control.
#7171 — scatter is NOT MEASURED, not clean. Its all-positive control drew zero marks on this instrument, so its zeros say nothing; scatter takes two measures and the sweep's single-measure fixture does not bind it. The implementer correctly declined to file it under the report-don't-duplicate fence and handed it back; I filed it. Now unblocked to
pm:queue.The footnote-visibility note carries forward, and it is not this PR's defect. A 520×240 tile — the box the card's own sweep used — is 40px shorter than
CHART_MIN_HEIGHT(280), so no footnote can be seen in it. Measured with #7161's landed note as the control:sankey--posNullis byte-identical before and after at 240, i.e. the already-shipped note is equally invisible there. A pre-existing property of sub-280 boxes, inherited and not worsened.Clearing
pm:dispatched.Generated by Claude Code