Repository navigation
feat(analytics): carry a dataset dimension's nullLabel on its column descriptor, and name System Overview's null user bucket "System" - #25402
Conversation
…descriptor The chart reads every column's display metadata from the dataset answer's fields[], so the null bucket's authored name rides there as fields[].nullLabel, resolved at the request locale like label. The System Overview audit dataset names its null user bucket "System". Claude-Session: https://claude.ai/code/session_016qAuAwFXMw94v5vFqTVcw2 Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016qAuAwFXMw94v5vFqTVcw2 Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 3 package(s): 13 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 4 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 144 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 61e22384c6b1b0463158d15f6984e56fed9b6a37 && git checkout 61e22384c6b1b0463158d15f6984e56fed9b6a37
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin c6bdcaccb7a3bd5715ddd461c156c16b3667490a b3dc0f513ffee547df3f8cdda0a047d7a61483e6 && git checkout -B drift-repro c6bdcaccb7a3bd5715ddd461c156c16b3667490a && git merge --no-ff b3dc0f513ffee547df3f8cdda0a047d7a61483e6
node scripts/docs-audit/affected-docs.mjs --json c6bdcaccb7a3bd5715ddd461c156c16b3667490a
|
…members Claude-Session: https://claude.ai/code/session_016qAuAwFXMw94v5vFqTVcw2 Co-authored-by: Claude <noreply@anthropic.com>
席位核对ACCEPT ·
Generated by Claude Code |
Requested by Tesla · project thread
Part of #25330
Clause-②: no
Metadata-protocol: none
The label half of #25330, landing 1 of 3 (seat-approved order). Ruled A (6358843349, maintainer 「同意」); the key itself landed in #25397 (
DatasetDimensionSchema.nullLabel). This PR carries the key to the renderer and declares it on System Overview. The objectui consumer follows with a.objectstack-shabump to this PR's merge commit, then a last objectstack PR flips thenullLabelliveness row to live and closes the card.Channel: the response's column descriptor, and why
Measured, not assumed. objectui's
DatasetWidgetstores the dataset answer'sfields[]and reads every column's display metadata from it (headerLabel,buildChartSeries). It never fetches the dataset document; its only metadata read is the base object's (/api/v1/meta/object/NAME, for option labels). So the null bucket's name rides the descriptor the renderer already reads, asfields[].nullLabel. The document channel would have added a new metadata read per widget beside it.That descriptor is typed by
@objectstack/spec(objectui'sDatasetResultFieldisAnalyticsResult['fields'][number]), so the carry needs one response member. It is response-only and nothing an author writes changes, soClause-②: noandMetadata-protocol: nonehold (seat ruling on the claim, declared on #18549).What changes
@objectstack/spec:AnalyticsResult.fields[](contracts/analytics-service.ts) and its mirrorAnalyticsResultResponseSchema(api/analytics.zod.ts) gainnullLabel?: string. The compile-time drift guard inanalytics.test.tskeeps the two equal; a new case pins that the parse preserves the member (the schema strips undeclared keys).@objectstack/service-analytics:enrichResultColumns, the column-description pass the live query and the draft-data preview share, setsfields[].nullLabelon a dimension column whose dataset dimension declaresnullLabel. It resolves throughresolveI18nLabelat the request locale, exactly likelabel, so the wire carries a string and never a locale map. It is written whether or not the header was already set, and never onto a measure column whose name matches a dimension's field. Absent ⇒ no key, so an answer over a dimension withoutnullLabelis byte-identical to before.@objectstack/platform-objects:sys_audit_log_metrics'user_iddimension declaresnullLabel: { en: 'System', 'zh-CN': '系统', 'ja-JP': 'システム', 'es-ES': 'Sistema' }, every locale the platform bundles translate the board into. An inline map is the ruled route: the translation bundles have no slot for a dimension's null label.check:i18nis in sync with no regeneration (the extractor does not readnullLabel).Pins
service-analytics/src/__tests__/dataset-dimension-null-label.test.ts, on both strategies: zh-CN reads系统, en readsSystem, no locale readsSystem, a plain string passes as authored; a dimension withoutnullLabeland every measure column (one of them over the sameuser_idfield) carry no key.platform-objects/src/apps/dashboards/system-overview-null-label.test.ts: "Events by User" groupssys_audit_log_metricsbyuser_id, the dimension carries the map, the map names every localeSetupAppTranslationscarries for the board, and it survives a strictDatasetSchemaparse.spec/src/api/analytics.test.ts: the response parse preservesfields[].nullLabel.Ablation (
scripts/ablation-replace.mjs, under the verify lock): the writeif (nullLabel !== undefined) f.nullLabel = nullLabel;replaced byvoid nullLabel;(anchor 1 → 0, blob changed) turns the service pin 6 failed / 2 passed; the 2 green are the no-key controls, green by design. Restored: blob equals HEAD andgit diff HEADis empty. The source is imported directly by the test, so no build leg applies.Verification
All at head
0c232759ab(base7c68b1082;origin/mainhas moved one commit since, touching none of these paths).pnpm --filter @objectstack/{service-analytics,platform-objects,spec} run typecheck: exit 0 (spec's runscheck:scripts-typecheckandcheck:test-typechecktoo).pnpm --filter @objectstack/service-analytics test: Test Files 185 passed (185), Tests 4513 passed, 262 skipped.pnpm --filter @objectstack/platform-objects test: Test Files 93 passed (93), Tests 1341 passed.pnpm --filter @objectstack/spec test: Test Files 711 passed (711), Tests 21634 passed, 1 todo. Lock VERDICT command-exit 0.pnpm --filter @objectstack/spec check:generated: all 14 artifacts up to date (no regeneration was needed).node scripts/pm/dispatch-gates.mjs --commands: 93 derived families, 92 run with exit 0, reconciled with--ran(0 unrun). 1 NOT MEASURED:pnpm check:dual-build-cjs-loadsexited 3, PREREQUISITE NOT MET, because it reads every package'sdist/and this worktree built only the affected closure; CI runs it in Lint & Repo Gates.pnpm check:i18n: exit 0, all 10 packages' bundles in sync.Acceptance notes
buildChartSeriestakes one null label per chart, used for both arms of a two-dimension pivot. The objectui consumer therefore applies a declared name on a pivot only when both dimensions declare the same one. Recorded in that PR as well.DatasetReportRendererandObjectChartin objectui still read onlychart.nullCategory; this card's consumer is the dashboard chart. They can read the same descriptor member later. carrier: none, noted only.—, not as the chart's bucket label, and is out of scope.Generated by Claude Code