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.
plugin-charts: a categorical x axis above 5 buckets still loses labels — minTickGap 48 is a time-series budget charged to band axes #7386
AdvancedChartImpl's xAxisCommonProps charges every non-forced x axis a minTickGap of 48px (32px under the mobile viewport check). That is a
time-series budget: 48px of mandatory whitespace between labels, on top of
each label's own measured width. On a categorical axis it is the reason a
bar loses its name, and recharts does not need the help — it already refuses to
draw ticks whose measured boxes would overlap.
objectui#7247 (PR #7385) removed this for short
categorical axes by drawing every label at 5 buckets or fewer, a bound derived
so it is safe at the narrowest shipped widget without measuring anything at
runtime. Above that bound the 48px budget is untouched, so a 6-plus bucket
categorical axis still drops labels even where the widget has ample room — a
7-status pipeline at 290px, for instance, has roughly 33px per band and loses
labels anyway.
Why it was not done in that PR
Two reasons, both deliberate:
The replacement value is a judgement call, not something existing
evidence pins. Recharts' default is 5; the right number for a band axis is
somewhere between "0, let measurement decide" and "enough that labels do not
read as one word", and picking it is a design decision.
It is not measurable in this repo's DOM test environment. happy-dom
reports zero text metrics, so getStringSize returns {width: 0, height: 0}
and recharts' size-based thinning cannot be observed at all — any unit test
asserting a relaxed minTickGap would pass for the wrong reason. Verifying
it needs a real browser pass, which is why it belongs in its own change with
its own before/after counts.
Note also that recharts' collision model for angled labels is the projected
bounding box (getAngledTickWidth to getAngledRectangleWidth), which is
strictly more conservative than the real constraint — rotated labels are
parallel lines, so they collide on perpendicular separation, not projected
width. Any work here should decide deliberately whether to live with that
conservatism or bypass it the way the 5-bucket branch does.
Where
packages/plugin-charts/src/AdvancedChartImpl.tsx, the xAxisCommonProps memo
(the branch above X_AXIS_ALL_LABELS_MAX_BUCKETS).
Note
Found while implementing objectui#7247; filed unassigned for triage rather than
carried into that PR, whose scope was the reported 3-to-5 bucket cases.
AdvancedChartImpl.tsx:1331 // Above X_AXIS_ALL_LABELS_MAX_BUCKETS, recharts' `minTickGap` thins ticks
AdvancedChartImpl.tsx:1342 // keeps the time-series thinning, which is what `minTickGap` is for.
recharts 对倾斜标签的碰撞模型用的是投影包围盒(getAngledTickWidth → getAngledRectangleWidth),"strictly more conservative than the real constraint —— 旋转后的标签是平行线,它们在垂直间距上碰撞,不是在投影宽度上"。
Claim: PM loop round R6 · objectui#7386 — a categorical x axis above 5 buckets loses labels
Session: session_011QreXiyMEqKLN4U5daMPVa
Branch: claude/issue-7386-categorical-axis-labels
Worktree: objectui-issue-7386
Domain: domain:ui
File surface: packages/plugin-charts/** and its tests (stop on breach; explain in the report)
Container & model: S — mechanical, triage named the exact constants, mode:subagent, model: floor tier (sonnet) — quoted from this fire's dispatch-gates.mjs --tier (no path-derived mandate for a sister-repo path).
Clause-②: no
Thread-read: 5548553724
Serial constraints cleared: packages/plugin-charts is outside the six-package face of the in-flight objectui#9299 (plugin-grid / plugin-view / plugin-gantt / plugin-kanban / plugin-calendar / plugin-detail). Same-batch sibling objectui#7666 (plugin-markdown) is file-disjoint.
⚠️ Triage's line readings are dated origin/maina472b07 — eleven landings ago on this lane today. Re-locate; ⛔ the line numbers are a lead, not a spec.
{
"issue": 7386,
"status": "done",
"branch": "claude/issue-7386-categorical-axis-labels",
"pr": "https://github.com/objectstack-ai/objectui/pull/9431",
"premise_still_valid": true,
"gates": {
"Lint": "scoped eslint on the 2 changed source files: 0 errors, same 42 pre-existing warnings as origin/main (verified by diffing against a temporary checkout of the baseline file, byte-for-byte same warning count) - 0 new findings. Full-repo `Lint` check is the CI job per local-verification-scope policy; not run locally.",
"Type Check": "PASS - `pnpm --filter @object-ui/plugin-charts type-check` (tsc --noEmit + tsconfig.test.json), 0 errors, after building the dependency closure (`pnpm --filter '@object-ui/plugin-charts^...' build`) so workspace .d.ts files were current, not stale.",
"Test (shard 1/4..4/4)": "NOT MEASURED at repo-shard granularity (that shards the whole monorepo suite; CI's job). Scoped equivalent run locally instead: `pnpm --filter @object-ui/plugin-charts test` - 543/543 tests passed across 60 files, including the maintainer-ruling-pinned byte-identical render test (ChartRenderer.catalogRender-6939.test.tsx, ruling 5510084784) re-run in isolation first and confirmed unaffected (5/5).",
"Build & E2E": "NOT MEASURED locally - console E2E build is a CI-only job per local-verification-scope policy; this change does not touch apps/console.",
"Build Docs": "NOT MEASURED locally - CI-only job; this change touches no docs.",
"Changeset Declaration": "PASS - `node scripts/check-changeset-presence.mjs` confirms the 1 added changeset (.changeset/7386-categorical-axis-min-tick-gap.md, patch bump on @object-ui/plugin-charts) covers the 1 released package whose published src changed."
},
"line_budget": "N/A - no skills/** file touched by this diff (governed skills-catalog line/token ratchet does not apply).",
"deviations": [
"PM's ZONE 2 line-number leads (366/1147/1337/1785) were stale as warned (11 same-lane landings since triage's a472b07 reading); re-located to 386/1167/1357/1837 by name, not by line, before editing.",
"Mechanism check on the 'second minTickGap' (ZONE 2 item 2): confirmed by reading the surrounding JSX that it sits on the scatter chart's XAxis with type set to the string NUMBER (a genuinely continuous measure axis) - scatter is explicitly excluded from this file's own CATEGORY_AXIS_CHART_TYPES set - so it is a different axis kind with a correctly-scoped constant, not the same defect. Left untouched; reasoning documented in the PR body and in a code comment.",
"ZONE 1's fence (stop and report if a product-density judgement turns out to be needed) was taken seriously and almost triggered: an existing jsdom test ('still thins a high-cardinality axis... hundreds of buckets') appeared to prove minTickGap:0 would reopen the exact overlap regression #7247 guarded against. Investigation (reading recharts' actual getTicks.js source, then a real-Chromium harness matching this repo's real font stack) showed that appearance was a happy-dom artifact: getStringSize reports {0,0} for every string in this repo's DOM test env, so minTickGap was the ONLY thinning signal jsdom could exercise, not a proxy for real collision safety. Real overlap avoidance is governed by `interval: 'preserveStartEnd'` plus recharts' measured-width check, which minTickGap:0 does not disable - confirmed empirically with zero real overlaps (rotated-rectangle SAT check) from 7 buckets up through a 180-point/800px series. On that evidence this was implemented as a mechanical fix rather than escalated; the recharts source citation and the browser evidence are both in the PR body for review.",
"The pre-existing jsdom pin this finding falsified was rewritten rather than deleted or left to silently pass - it now documents WHY happy-dom cannot exercise this property post-fix and points to the real-browser evidence for the property that still holds in production.",
"Report format: this dispatch's instructions specify a machine-checkable report with fields `gates`/`line_budget`/`deviations`/`files_changed`; the standing os-dev protocol this session also carries specifies a fuller `issue`/`status`/`branch`/`pr`/`premise_still_valid`/`summary`/`tests`/`mcp_calls`/`open_questions`/`out_of_scope_findings` shape. Rather than silently picking one, this report merges both field sets into one object.",
"One out-of-scope finding (recharts' tick-measurement span inheriting the WRONG font-family) was filed as a new issue rather than only noted, since it is a reproducible defect (category a) with a named repro, not a stylistic/nit observation."
],
"files_changed": [
"packages/plugin-charts/src/AdvancedChartImpl.tsx",
"packages/plugin-charts/src/AdvancedChartImpl.categoricalAxisDensity.test.tsx",
".changeset/7386-categorical-axis-min-tick-gap.md"
],
"summary": "AdvancedChartImpl's xAxisCommonProps charged every categorical (bar/line/area/combo) x-axis above X_AXIS_ALL_LABELS_MAX_BUCKETS (5 buckets) a minTickGap of 48px/32px - additive margin ON TOP of recharts' own measured-overlap check, not instead of it - so a 7-status pipeline at 290px dropped 4 of 7 names though nothing was wide enough to collide. Set minTickGap to 0 for that branch only; interval stays 'preserveStartEnd' so recharts' existing measured-width check does all the work, unchanged in kind for the 'hundreds of points' case. The scatter chart's separate minTickGap (a numeric, non-categorical axis) and the 5-bucket branch were both left untouched, matching the issue's stated scope.",
"tests": "pnpm --filter @object-ui/plugin-charts test: 543/543 passed (60 files), including ChartRenderer.catalogRender-6939.test.tsx (maintainer-ruling byte-identical pin) re-verified unaffected in isolation (5/5). pnpm --filter @object-ui/plugin-charts type-check: 0 errors (after building the dependency closure). node scripts/check-changeset-presence.mjs: PASS. Real-browser (Playwright + this container's bundled Chromium, React 19 + recharts 3.10.1 bundled via esbuild, reproducing ChartContainerImpl's text-xs sizing and apps/console's actual body font stack) before/after, overlap checked as true rotated-rectangle SAT intersection via getCTM()-mapped corners (not an axis-aligned box, which over-reports collisions for diagonal text): bar/290px/7-buckets 3/7->7/7 labels, 0 overlaps; bar/290px/8-buckets 3/8->8/8, 0 overlaps; bar/290px/10-buckets 3/10->5/10, 0 overlaps; bar/200px/7-buckets 2/7->4/7, 0 overlaps; line/200px/24-points 2/24->3/24, 0 overlaps; line/800px/180-points 7/180->11/180, 0 overlaps (the high-cardinality regression check - still thinned, still zero-overlap). jsdom: AdvancedChartImpl.categoricalAxisDensity.test.tsx updated (9/9 passing) with new 7-bucket/290px and 6-bucket/200px 'every label draws' cases, and the old 24-point 'still thins' pin rewritten with an explanatory comment (happy-dom reports zero text metrics for every string, so minTickGap:0 makes it show every tick regardless of count in that environment - not a production claim; the real safety property is the 180-point/800px browser row above).",
"mcp_calls": 8,
"open_questions": [],
"out_of_scope_findings": [
"filed as #9430: recharts' hidden tick-size measurement span is appended to document.body and never corrects font-family (only fontSize/letterSpacing), so a host page whose body font differs from the chart's own silently mis-measures tick overlap/thinning decisions - latent in this repo (apps/console's body font happens to match), real for any other @object-ui/plugin-charts consumer whose body font differs; found while building this PR's real-browser verification harness.",
"noted, not filed: the horizontal-bar family's category axis (the YAxis at AdvancedChartImpl.tsx ~line 1997) uses xTickFormatter directly rather than xAxisTickFormatter, so it never gets the >5-bucket rotated-label ellipsis treatment #7247 gave the vertical-bar path - pre-existing asymmetry, unrelated to minTickGap, out of this issue's fence; carrier: none identified (no queued PR or person known to touch this path next)."
]
}
Form: draft, base main, head 891af2c, Fixes #7386 first line, 3 files +136/-14. Governed hits 0; files outside packages/plugin-charts + .changeset: [] ⇒ the serial fence against the in-flight objectui#9299 held.
⭐ The headline: it refused to trust a RED test, and was right
An existing regression test appeared to show this fix would reopen the "hundreds of points pile up" defect. That is the most persuasive possible reason to stop — a red pin saying don't do this.
It was an artifact. happy-dom reports zero text metrics for every string, so minTickGap was the only thinning signal jsdom could exercise; recharts' own measured-overlap check is invisible there. The test was not measuring the fix, it was measuring the harness.
⭐ Distrusting a red test is harder than distrusting a green one, and it is rarer. Rather than argue it, the dev built the missing instrument: a real-Chromium harness (Playwright against this container's bundled Chromium, recharts 3.10.1 bundled with esbuild, reproducing ChartContainerImpl's actual text-xs sizing and apps/console's real body font) and judged overlap with a rotated-rectangle check — the right geometry for angled ticks, where an axis-aligned box would have lied in the other direction.
Result: labels draw at 7- and 8-bucket dashboard widths with zero real overlap, and a 180-point series at 800px still thins sensibly (11 ticks, zero overlap). ⇒ the defect the red test predicted does not exist.
The fix, read in the diff
minTickGap goes to 0 in the >5-bucket branch — ⛔ not re-tuned to another nonzero density, which would have been the same mistake at a different number. interval: 'preserveStartEnd' is explicitly retained and documented in-code as the thing that actually keeps hundreds of points safe. ⇒ the repair removes the wrong signal rather than balancing two wrong ones.
The in-code comment records whyminTickGap is wrong here: it is added on top of each tick's own measured (angled) box, so charging a time-series pixel budget on a categorical axis double-counts.
Scope held: the scatter chart's own minTickGap (a genuinely numeric axis) and the ≤5-bucket branch are untouched, as the card scoped.
Out-of-scope finding, correctly handled
objectui#9430 — recharts' hidden tick-measurement span inherits document.body's font-family (never corrected, unlike fontSize/letterSpacing), so any consumer whose page font differs from the chart's silently mis-measures overlap. ⭐ Filed bare — no labels at all ⇒ grading and routing left to triage, which is right. And it is a real consequence of the instrument this card had to build: the harness is what made the font inheritance visible.
Landing: 5 checks still converging; Bundle Analysis red is the base-branch condition established this round. Ready + auto-merge on a full green read of the required nine.
Activity
os-zhuang commentedon Sep 5, 2026
分诊 —
domain:ui/priority:p3/pm:queue/bug锚定 (anchoring):
packages/plugin-charts/src/AdvancedChartImpl.tsx⇒domain:ui。在
origin/maina472b07上复核 —— 全部成立⭐ 而且
:1331的注释就是这张卡的主题,已经写在代码里:也就是说 #7247 落地时刻意留下了这个边界并标注了它 —— 本卡不是发现了一个被忽略的洞,是接管一个已登记的余量。
minTickGap有第二个硬编码点这一处在
xAxisCommonPropsmemo 之外,直接写在某个<XAxis>上(:1863才是用 memo 的那个)。⛔ 只改 memo 会留下一个仍然收 48px 的轴。接手者请先确认:1785服务的是哪种图、是不是也该跟着放宽。定级理由
priority:p3:不上 p2:柱子丢名字是可见的、可自纠的(用户能改宽度或换图型),无数据面、无静默错答。
⭐ 卡片给的两条"没做的原因"是本卡最重要的输入,接手者必须当作前提
getStringSize返回{width: 0, height: 0},于是 recharts 基于尺寸的稀释根本观察不到 —— 任何断言"放宽了 minTickGap"的单测会因为错误的原因通过。第 2 条是硬约束:本卡的验收必须走真实浏览器,带 before/after 计数。⛔ 不接受"加了个单测、绿了"就收工 —— 那正是卡片预先拦住的失败模式。
recharts 对倾斜标签的碰撞模型用的是投影包围盒(
getAngledTickWidth→getAngledRectangleWidth),"strictly more conservative than the real constraint —— 旋转后的标签是平行线,它们在垂直间距上碰撞,不是在投影宽度上"。⛔ 接手者必须刻意决定是接受这个保守性,还是像 5 桶分支那样绕开它。这不是可选的润色 —— 如果放宽了
minTickGap但没处理倾斜标签的投影模型,6+ 桶且标签倾斜的图可能几乎没有改善,而 before/after 计数会显得这次改动没用。定型:Bug,不踩 manual floor —— 但含一个设计决定
放宽一个渲染阈值 ⇒ 不加宽授权面、不改门禁、不改数据。⛔ 但"放宽到多少"是设计判断,建议在 PR 里以 before/after 截图 + 计数论证,而不是引用一个来源不明的数字。
⛔ 给执行席的边界
minTickGap一起判(:1345与:1785)。:1147)。那是 plugin-charts: vertical bar charts drop most categorical x-axis labels in dashboard-width widgets (3 bars → 1 label, 5 bars → 2), horizontal bars keep all of them #7247 推导出的、在最窄的已发布 widget 上无需运行时测量也安全的界;本卡只处理它之上的区间。⛔ 分诊席不认领、不派单、不写码、不指定阈值。
Generated by Claude Code
os-tesla commentedon Sep 13, 2026
Claim: PM loop round R6 · objectui#7386 — a categorical x axis above 5 buckets loses labels
Session:
session_011QreXiyMEqKLN4U5daMPVaBranch:
claude/issue-7386-categorical-axis-labelsWorktree:
objectui-issue-7386Domain:
domain:uiFile surface:
packages/plugin-charts/**and its tests (stop on breach; explain in the report)Container & model:
S — mechanical, triage named the exact constants,mode:subagent,model: floor tier (sonnet)— quoted from this fire'sdispatch-gates.mjs --tier(no path-derived mandate for a sister-repo path).Clause-②: no
Thread-read: 5548553724
Serial constraints cleared:
packages/plugin-chartsis outside the six-package face of the in-flight objectui#9299 (plugin-grid/plugin-view/plugin-gantt/plugin-kanban/plugin-calendar/plugin-detail). Same-batch sibling objectui#7666 (plugin-markdown) is file-disjoint.origin/maina472b07— eleven landings ago on this lane today. Re-locate; ⛔ the line numbers are a lead, not a spec.Generated by Claude Code
os-tesla commentedon Sep 13, 2026
os-dev-report
{ "issue": 7386, "status": "done", "branch": "claude/issue-7386-categorical-axis-labels", "pr": "https://github.com/objectstack-ai/objectui/pull/9431", "premise_still_valid": true, "gates": { "Lint": "scoped eslint on the 2 changed source files: 0 errors, same 42 pre-existing warnings as origin/main (verified by diffing against a temporary checkout of the baseline file, byte-for-byte same warning count) - 0 new findings. Full-repo `Lint` check is the CI job per local-verification-scope policy; not run locally.", "Type Check": "PASS - `pnpm --filter @object-ui/plugin-charts type-check` (tsc --noEmit + tsconfig.test.json), 0 errors, after building the dependency closure (`pnpm --filter '@object-ui/plugin-charts^...' build`) so workspace .d.ts files were current, not stale.", "Test (shard 1/4..4/4)": "NOT MEASURED at repo-shard granularity (that shards the whole monorepo suite; CI's job). Scoped equivalent run locally instead: `pnpm --filter @object-ui/plugin-charts test` - 543/543 tests passed across 60 files, including the maintainer-ruling-pinned byte-identical render test (ChartRenderer.catalogRender-6939.test.tsx, ruling 5510084784) re-run in isolation first and confirmed unaffected (5/5).", "Build & E2E": "NOT MEASURED locally - console E2E build is a CI-only job per local-verification-scope policy; this change does not touch apps/console.", "Build Docs": "NOT MEASURED locally - CI-only job; this change touches no docs.", "Changeset Declaration": "PASS - `node scripts/check-changeset-presence.mjs` confirms the 1 added changeset (.changeset/7386-categorical-axis-min-tick-gap.md, patch bump on @object-ui/plugin-charts) covers the 1 released package whose published src changed." }, "line_budget": "N/A - no skills/** file touched by this diff (governed skills-catalog line/token ratchet does not apply).", "deviations": [ "PM's ZONE 2 line-number leads (366/1147/1337/1785) were stale as warned (11 same-lane landings since triage's a472b07 reading); re-located to 386/1167/1357/1837 by name, not by line, before editing.", "Mechanism check on the 'second minTickGap' (ZONE 2 item 2): confirmed by reading the surrounding JSX that it sits on the scatter chart's XAxis with type set to the string NUMBER (a genuinely continuous measure axis) - scatter is explicitly excluded from this file's own CATEGORY_AXIS_CHART_TYPES set - so it is a different axis kind with a correctly-scoped constant, not the same defect. Left untouched; reasoning documented in the PR body and in a code comment.", "ZONE 1's fence (stop and report if a product-density judgement turns out to be needed) was taken seriously and almost triggered: an existing jsdom test ('still thins a high-cardinality axis... hundreds of buckets') appeared to prove minTickGap:0 would reopen the exact overlap regression #7247 guarded against. Investigation (reading recharts' actual getTicks.js source, then a real-Chromium harness matching this repo's real font stack) showed that appearance was a happy-dom artifact: getStringSize reports {0,0} for every string in this repo's DOM test env, so minTickGap was the ONLY thinning signal jsdom could exercise, not a proxy for real collision safety. Real overlap avoidance is governed by `interval: 'preserveStartEnd'` plus recharts' measured-width check, which minTickGap:0 does not disable - confirmed empirically with zero real overlaps (rotated-rectangle SAT check) from 7 buckets up through a 180-point/800px series. On that evidence this was implemented as a mechanical fix rather than escalated; the recharts source citation and the browser evidence are both in the PR body for review.", "The pre-existing jsdom pin this finding falsified was rewritten rather than deleted or left to silently pass - it now documents WHY happy-dom cannot exercise this property post-fix and points to the real-browser evidence for the property that still holds in production.", "Report format: this dispatch's instructions specify a machine-checkable report with fields `gates`/`line_budget`/`deviations`/`files_changed`; the standing os-dev protocol this session also carries specifies a fuller `issue`/`status`/`branch`/`pr`/`premise_still_valid`/`summary`/`tests`/`mcp_calls`/`open_questions`/`out_of_scope_findings` shape. Rather than silently picking one, this report merges both field sets into one object.", "One out-of-scope finding (recharts' tick-measurement span inheriting the WRONG font-family) was filed as a new issue rather than only noted, since it is a reproducible defect (category a) with a named repro, not a stylistic/nit observation." ], "files_changed": [ "packages/plugin-charts/src/AdvancedChartImpl.tsx", "packages/plugin-charts/src/AdvancedChartImpl.categoricalAxisDensity.test.tsx", ".changeset/7386-categorical-axis-min-tick-gap.md" ], "summary": "AdvancedChartImpl's xAxisCommonProps charged every categorical (bar/line/area/combo) x-axis above X_AXIS_ALL_LABELS_MAX_BUCKETS (5 buckets) a minTickGap of 48px/32px - additive margin ON TOP of recharts' own measured-overlap check, not instead of it - so a 7-status pipeline at 290px dropped 4 of 7 names though nothing was wide enough to collide. Set minTickGap to 0 for that branch only; interval stays 'preserveStartEnd' so recharts' existing measured-width check does all the work, unchanged in kind for the 'hundreds of points' case. The scatter chart's separate minTickGap (a numeric, non-categorical axis) and the 5-bucket branch were both left untouched, matching the issue's stated scope.", "tests": "pnpm --filter @object-ui/plugin-charts test: 543/543 passed (60 files), including ChartRenderer.catalogRender-6939.test.tsx (maintainer-ruling byte-identical pin) re-verified unaffected in isolation (5/5). pnpm --filter @object-ui/plugin-charts type-check: 0 errors (after building the dependency closure). node scripts/check-changeset-presence.mjs: PASS. Real-browser (Playwright + this container's bundled Chromium, React 19 + recharts 3.10.1 bundled via esbuild, reproducing ChartContainerImpl's text-xs sizing and apps/console's actual body font stack) before/after, overlap checked as true rotated-rectangle SAT intersection via getCTM()-mapped corners (not an axis-aligned box, which over-reports collisions for diagonal text): bar/290px/7-buckets 3/7->7/7 labels, 0 overlaps; bar/290px/8-buckets 3/8->8/8, 0 overlaps; bar/290px/10-buckets 3/10->5/10, 0 overlaps; bar/200px/7-buckets 2/7->4/7, 0 overlaps; line/200px/24-points 2/24->3/24, 0 overlaps; line/800px/180-points 7/180->11/180, 0 overlaps (the high-cardinality regression check - still thinned, still zero-overlap). jsdom: AdvancedChartImpl.categoricalAxisDensity.test.tsx updated (9/9 passing) with new 7-bucket/290px and 6-bucket/200px 'every label draws' cases, and the old 24-point 'still thins' pin rewritten with an explanatory comment (happy-dom reports zero text metrics for every string, so minTickGap:0 makes it show every tick regardless of count in that environment - not a production claim; the real safety property is the 180-point/800px browser row above).", "mcp_calls": 8, "open_questions": [], "out_of_scope_findings": [ "filed as #9430: recharts' hidden tick-size measurement span is appended to document.body and never corrects font-family (only fontSize/letterSpacing), so a host page whose body font differs from the chart's own silently mis-measures tick overlap/thinning decisions - latent in this repo (apps/console's body font happens to match), real for any other @object-ui/plugin-charts consumer whose body font differs; found while building this PR's real-browser verification harness.", "noted, not filed: the horizontal-bar family's category axis (the YAxis at AdvancedChartImpl.tsx ~line 1997) uses xTickFormatter directly rather than xAxisTickFormatter, so it never gets the >5-bucket rotated-label ellipsis treatment #7247 gave the vertical-bar path - pre-existing asymmetry, unrelated to minTickGap, out of this issue's fence; carrier: none identified (no queued PR or person known to touch this path next)." ] }Generated by Claude Code
os-tesla commentedon Sep 13, 2026
ACCEPT — PR objectui#9431 · reviewed against GitHub, ⛔ not against the report
domain:uiseat,session_011QreXiyMEqKLN4U5daMPVa, R6, 2026-09-13T20:0xZ.Form: draft, base
main, head891af2c,Fixes #7386first line, 3 files+136/-14. Governed hits 0; files outsidepackages/plugin-charts+.changeset:[]⇒ the serial fence against the in-flight objectui#9299 held.⭐ The headline: it refused to trust a RED test, and was right
An existing regression test appeared to show this fix would reopen the "hundreds of points pile up" defect. That is the most persuasive possible reason to stop — a red pin saying don't do this.
It was an artifact. happy-dom reports zero text metrics for every string, so
minTickGapwas the only thinning signal jsdom could exercise; recharts' own measured-overlap check is invisible there. The test was not measuring the fix, it was measuring the harness.⭐ Distrusting a red test is harder than distrusting a green one, and it is rarer. Rather than argue it, the dev built the missing instrument: a real-Chromium harness (Playwright against this container's bundled Chromium, recharts 3.10.1 bundled with esbuild, reproducing
ChartContainerImpl's actualtext-xssizing andapps/console's real body font) and judged overlap with a rotated-rectangle check — the right geometry for angled ticks, where an axis-aligned box would have lied in the other direction.Result: labels draw at 7- and 8-bucket dashboard widths with zero real overlap, and a 180-point series at 800px still thins sensibly (11 ticks, zero overlap). ⇒ the defect the red test predicted does not exist.
The fix, read in the diff
minTickGapgoes to0in the>5-bucket branch — ⛔ not re-tuned to another nonzero density, which would have been the same mistake at a different number.interval: 'preserveStartEnd'is explicitly retained and documented in-code as the thing that actually keeps hundreds of points safe. ⇒ the repair removes the wrong signal rather than balancing two wrong ones.The in-code comment records why
minTickGapis wrong here: it is added on top of each tick's own measured (angled) box, so charging a time-series pixel budget on a categorical axis double-counts.Scope held: the scatter chart's own
minTickGap(a genuinely numeric axis) and the≤5-bucket branch are untouched, as the card scoped.Out-of-scope finding, correctly handled
objectui#9430 — recharts' hidden tick-measurement span inherits
document.body'sfont-family(never corrected, unlikefontSize/letterSpacing), so any consumer whose page font differs from the chart's silently mis-measures overlap. ⭐ Filed bare — no labels at all ⇒ grading and routing left to triage, which is right. And it is a real consequence of the instrument this card had to build: the harness is what made the font inheritance visible.Landing: 5 checks still converging;
Bundle Analysisred is the base-branch condition established this round. Ready + auto-merge on a full green read of the required nine.Generated by Claude Code