Analytics: a dataset dimension on a dotted relationship path (crm_account.industry) renders raw stored values — the same field as a local dimension renders its option labels #4053
Description
Activity
CLAIM (objectui whole-repo seat PM, session
session_017Qqyix2QcnpUC9XeYVDzx3) — dispatching now.- Branch
claude/issue-4053-dotted-dimension-labels, worktreeobjectui-issue-4053(M container) - Scope per the card's suggested fix: a dotted dimension
fieldresolves its select options against the relationship's TARGET object, then flows through the same option-label resolution the local path uses (one resolver, not a dotted-path fork). The i18n-bundle half stays with objectstack#5076 — this card ends at "the label is in hand", per its own not-a-duplicate reasoning. Source thread objectstack#5144 is required reading per the migration header.
Generated by Claude Code
- Branch
ACCEPT (objectui seat PM, session
session_017Qqyix2QcnpUC9XeYVDzx3, review of record) — PR #4261, CI fully green in one converged pass.The card asked for one site's fix; the measurement found TWO carrier sites and the fix landed as ONE shared resolver beside its sibling helpers, both consumers converged — the anti-drift shape, again. The dispatch's stop-condition (no new fetch layer on a guess) was answered the right way: target-object metadata rides the EXISTING
GET /meta/object/:namechannel through a caller-suppliedloadObjectSchema(inheriting auth, base-URL rewrite, and draft-preview per #4121), memoized and seeded so sibling dimensions sharing a prefix fetch once and the base is never re-read. Two safety details deserve the record: relationship canonicalization mirrors the dataset designer's ownresolveReferenceTo(all four spellings), and the walk is GATED onlookup/master_detailso a plain field's segment can never be coerced into an object name and speculatively fetched. Multi-hop came free, is pinned, and is proven non-hypothetical (ADR-0071's picker builds 2-hop paths that reach this resolver today). Scope held exactly at "the label is in hand" per the card's own dedup reasoning. The reverse verification's one prediction deviation (the fetch-trace assertion moving with the walk while the result assertion held) is reported, not smoothed. #4263 (the table/pivot/metric early-return leaves dotted dimensions raw on those widgets — a real user-visible remainder) is correctly NOT ridden — enabling the client net there adds a resolution those widgets never had, a design call — and the seat is queueing it now. Flipping ready + auto-merge.
Generated by Claude Code
Summary
A
DatasetDimensionwhosefieldis a dotted relationship path gets no select-option resolution at all: the chart plots the raw stored values (education,finance,manufacturing). The same underlying field, reached as a local dimension on the object that owns it, resolves to its option labels (Education,Finance,Manufacturing).Both render side by side on one dashboard, so the inconsistency is visible without leaving the screen.
Evidence
@objectstack/*17.0.0-rc.1, fresh database, admin user. One dashboard (executive_dashboard), two widgets, both grouping oncrm_account.industry:Widget A — joined dimension → raw values
where the dataset declares
renders its legend as:
Widget B — local dimension → option labels
where the dataset declares
renders its rows as:
Same field, same dashboard, same request. The only difference is
field: 'crm_account.industry'vsfield: 'industry'.The option values on
crm_account.industryareeducation/finance/ … with labelsEducation/Finance/ …, so widget A's output is exactly the stored column, unresolved.Mechanism
The label resolver appears to look the select options up on the dataset's own object. For a dotted
field, the options live on the related object (crm_account), the lookup misses, and the renderer falls through to the stored value. Nothing errors, so the widget just quietly displays database enum values to end users.DatasetDimensionhas no key to help here — its whole surface is{ name, label, field, type, dateGranularity }andtypehas noselectmember — so the dataset cannot declare "this dotted path is a picklist, resolve it againstcrm_account".Why this is not a duplicate of objectstack-ai/objectstack#5076
objectstack-ai/objectstack#5076 is about analytics surfaces resolving option labels and then not applying the i18n bundle — the label is found, it is just the English one. That is widget B's behaviour.
This issue is the layer below: for a joined dimension the label is never resolved at all. Fixing objectstack-ai/objectstack#5076 alone would leave widget A still printing
education, because there is no label in hand to translate.Impact
Raw enum values reach end users on a dashboard. On a non-English deployment it is worse than English —
educationis neither the user's language nor a word they have ever seen in the UI, since every form and list shows the translated label.Suggested fix
When a dimension's
fieldis a dotted path, resolve the select options against the target object of the relationship rather than the dataset's base object, then apply the same option-label (and, per objectstack-ai/objectstack#5076, i18n bundle) resolution the local-dimension path already uses.Related
nameas the axis title, ignoring the measure'slabel— the same measure on a dashboard chart resolves correctly objectstack#5141 — report charts drop the dataset measure'slabel.resolveViewLabelreads two fields the served view document does not have objectstack#4854 — view / report / dataset labels have no live translation path.Filed from a downstream app (
hotcrm-heimao), tracked there as issue #69.