Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .changeset/25330-null-label-live.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
'@objectstack/spec': patch
---

fix(spec): a dataset dimension's `nullLabel` is live, so `os lint` stops warning that nothing draws it, and the dataset form offers it

Clause-②: no (a liveness-ledger re-grade plus a form column for a key the schema already accepts: no schema, accept set, served body or error code moves)
Metadata-protocol: none

The console now draws a dataset chart's null bucket under the name its dimension declares: the server carries it on the answer's column descriptor as `fields[].nullLabel` (objectstack#25402), and the dashboard's dataset chart reads it (objectui#13354, at the `.objectui-sha` pin this release builds). So:

- **Liveness:** `liveness/dataset.json` re-grades `dimensions[].nullLabel` from `planned` to `live`, citing the console's reader at the pin and the server's carry as its producer. Its `authorWarn` goes with it, so the advisory lint no longer tells an author who sets `nullLabel` that no renderer draws the bucket under it.
- **Dataset form:** the Dimensions repeater gains a Null Label column, the schema's own `.meta({ title })`. The form had left the key out only while nothing drew it. That reason went with the `planned` verdict, so its row in the form ↔ Zod reconciliation ledger is gone.
18 changes: 18 additions & 0 deletions .changeset/console-1d178e06ac5e.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
"@objectstack/console": patch
---

Console (objectui) refreshed to `1d178e06ac5e`. Frontend changes in this range:

Derived from the changesets objectui declared over the range — 6 releasing of 6 changesets added across 5 non-merge commits.

- **minor** — Setup → Positions now says who holds each position and which permission sets it grants, and a click anywhere on a row opens the item (objectstack#25333). (objectui `a14737b14`)
- **patch** — types: a node derived from an `@objectstack/spec` schema keeps the spec's `credential` registry key (`'value' | 'map'`, objectstack#25036, which marks the webhook's `secret` and `… (objectui `1d178e06a`)
- **patch** — dashboard: a dataset chart draws its null bucket under the name the dataset declares for it (`fields[].nullLabel`, the dimension's `nullLabel` resolved by the server), over the gl… (objectui `1d178e06a`)
- **patch** — Setup's Permission Sets and Positions lists ask before the Active switch turns an item off, and a toast confirms the change either way (objectui#13338). (objectui `fcbf1b280`)
- **patch** — A member who may read none of the objects an app names now lands on one page that says they need access and whom to ask, instead of the app's dashboard drawn as a grid of "You don… (objectui `2c4e5f5af`)
- **patch** — A single-series bar chart draws every bar in its series colour (objectstack#25330). (objectui `56b65c0ce`)

Metadata-protocol: none (the console refresh changes no key, value or type a metadata author writes)

objectui range: `5fef032b1ada...1d178e06ac5e`
2 changes: 1 addition & 1 deletion .objectui-sha
Original file line number Diff line number Diff line change
@@ -1 +1 @@
5fef032b1ada5b86bad640637e7331e26ee5da6e
1d178e06ac5eda824af06f7d68eaccdcf38dcf63
Original file line number Diff line number Diff line change
Expand Up @@ -2039,6 +2039,9 @@ export const enMetadataForms: NonNullable<TranslationData['metadataForms']> = {
"dimensions.dateGranularity": {
label: "Date Granularity"
},
"dimensions.nullLabel": {
label: "Null Label"
},
measures: {
label: "Measures",
helpText: "Each: name, aggregate, field (optional for count), and display format/currency"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2039,6 +2039,9 @@ export const esESMetadataForms: NonNullable<TranslationData['metadataForms']> =
"dimensions.dateGranularity": {
label: "Granularidad de fecha"
},
"dimensions.nullLabel": {
label: "Etiqueta de nulos"
},
measures: {
label: "Medidas",
helpText: "Cada medida: nombre, agregación, campo (opcional para count) y formato de visualización/moneda"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2039,6 +2039,9 @@ export const jaJPMetadataForms: NonNullable<TranslationData['metadataForms']> =
"dimensions.dateGranularity": {
label: "日付の粒度"
},
"dimensions.nullLabel": {
label: "空値ラベル"
},
measures: {
label: "メジャー",
helpText: "各メジャー:名前、集計関数、フィールド(count の場合は省略可)、表示形式/通貨"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1212,7 +1212,10 @@ describe('#19403 round 10 — the verdicts, on the live bundles', () => {
// composites spell out their two live sub-rows (`enabled`, `position`;
// `mode` / `formView` were retired), four new row labels authored in all
// three locales.
expect(translated.length, `${locale} positive control`).toBe(677);
// 678 since #25330: the dataset form's dimensions repeater offers
// `nullLabel` once its liveness verdict went live — one row label,
// authored in all three locales.
expect(translated.length, `${locale} positive control`).toBe(678);
}
// ⭐ DARK — the blindness, executable. On a synthetic two-locale catalog the
// all-three predicate returns 0 while the per-locale one returns 1, so the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
// echoes in two locales TODAY. It is OUT of this population, asserted by
// name — a walk that over-reached into the object form would sweep in 32
// undecided leaves, and the assertion is what notices. `datasetForm` —
// the sibling semantic-layer panel, 37 leaves, 0 echoing — is excluded
// the sibling semantic-layer panel, 38 leaves, 0 echoing — is excluded
// too, and that exclusion is asserted rather than trusted precisely
// because a wrong sweep of it would NOT go red.
// (2) INWARD, AUTHORED. 38 of this population's own 44 leaves come back
Expand Down Expand Up @@ -888,7 +888,8 @@ describe('#19403 round 9 — the population, DERIVED from `reportForm`', () => {
// …and `datasetForm`, the sibling semantic-layer panel, is excluded too.
// It is fully authored, so a wrong sweep of it would NOT go red — which is
// exactly why the exclusion is asserted instead of trusted.
expect(DATASET_LEAVES.length).toBe(37);
// 38 since #25330: the dimensions repeater offers `nullLabel`.
expect(DATASET_LEAVES.length).toBe(38);
const datasetEchoes = DATASET_LEAVES.filter((l) =>
TRANSLATED_LOCALES.some(([, forms]) => forms.dataset?.[l.kind === 'section' ? 'sections' : 'fields']?.[l.path]?.[l.prop] === l.en),
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2039,6 +2039,9 @@ export const zhCNMetadataForms: NonNullable<TranslationData['metadataForms']> =
"dimensions.dateGranularity": {
label: "日期粒度"
},
"dimensions.nullLabel": {
label: "空值标签"
},
measures: {
label: "度量",
helpText: "每个度量:名称、聚合函数、字段(count 时可省略)、显示格式/货币"
Expand Down
6 changes: 3 additions & 3 deletions packages/sdui-parser/objectui-lockstep.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
],
"objectui": {
"repo": "https://github.com/objectstack-ai/objectui.git",
"rev": "5fef032b1ada5b86bad640637e7331e26ee5da6e",
"revDate": "2026-10-22T18:39:55+00:00",
"rev": "1d178e06ac5eda824af06f7d68eaccdcf38dcf63",
"revDate": "2026-10-23T12:16:26+00:00",
"source": "packages/sdui-parser/src",
"files": [
"packages/sdui-parser/src/body-dialect.ts",
Expand All @@ -21,7 +21,7 @@
"packages/sdui-parser/src/validate.ts"
]
},
"recordedAgainstPin": "5fef032b1ada5b86bad640637e7331e26ee5da6e",
"recordedAgainstPin": "1d178e06ac5eda824af06f7d68eaccdcf38dcf63",
"grammarRegion": {
"file": "packages/sdui-parser/src/parse.ts",
"delimiter": "/* ---------------------- the JS literal subset (#6614) ---------------------- */",
Expand Down
8 changes: 4 additions & 4 deletions packages/spec/liveness/dataset.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,12 @@
"verifiedAt": "2026-08-28"
},
"nullLabel": {
"status": "planned",
"status": "live",
"verifiedAt": "2026-10-23",
"evidenceScope": "cross-repo",
"authorWarn": true,
"authorHint": "Keep it, but nothing reads it yet: no chart renderer draws a dimension's null bucket under its `nullLabel`, so the bucket still shows the renderer's own default label until the renderer half ships.",
"note": "PLANNED RECORD (#25391, 2026-10-23): declared spec-first by ruling 6358843349 on #25330 (letter A), whose consumer half follows under #25330 — the objectui chart renderer reads `nullLabel` over its global `chart.nullCategory` default, and `packages/platform-objects` `system.datasets.ts` sets `user_id`'s null label. Display copy in the `label` slot's own type (I18nLabelSchema); it changes no grouping. No reader on either side today: objectstack — the dataset compiler (packages/services/service-analytics/src/dataset-compiler.ts#compileDataset) copies `name` / `label` / `field` / `type` / `dateGranularity` into the Cube dimension and nothing else, and a dataset response's dimension descriptor is `{ name, type, label }`; objectui @0d12e953 (the `.objectui-sha` pin) and main @a14737b — the only `nullLabel` spellings are the local parameter of objectui: packages/core/src/utils/chart-series.ts#pivotSeriesBuckets, fed from `ChartSeriesOptions.nullCategoryLabel ?? NULL_CATEGORY_LABEL`, never from a dataset dimension. Which channel carries the key to the renderer (the served dataset document or the response's dimension descriptor) is the consumer half's decision, not this row's."
"evidence": "objectui @1d178e06ac5e (the `.objectui-sha` pin, objectui#13354) packages/plugin-dashboard/src/DatasetWidget.tsx#declaredNullCategoryLabel reads the category dimension's `fields[].nullLabel` off the dataset answer, and the widget passes it as the chart's `nullCategoryLabel` over its global `chart.nullCategory` default, so the null bucket is drawn, and drilled, under the declared name. On a two-dimension pivot it applies only when both dimensions declare the same name (`buildChartSeries` takes one null label for both arms) — re-read 2026-10-23",
"producer": "packages/services/service-analytics/src/analytics-service.ts#enrichResultColumns — the column-description pass shared by the live query and the draft-data preview resolves the dimension's `nullLabel` at the request locale (resolveI18nLabel, the `label` resolver) and writes it onto the dimension column's descriptor as `fields[].nullLabel` (objectstack#25402), the channel the renderer reads; a measure column never carries it",
"note": "FLIPPED planned → live 2026-10-23 (#25330, ruling 6358843349 letter A), the `print` (#23710) and `runAction` (#10068) shape: declared spec-first by #25391 with `authorWarn`, its consumer landed in three steps — objectstack#25402 carries the name on the answer's column descriptor (the producer), objectui#13354 draws the bucket under it, and this change moves `.objectui-sha` past that merge and re-measures at the pin. `authorWarn` goes with the flip. Display copy only: grouping is unchanged. First author: `packages/platform-objects` `system.datasets.ts`, whose `user_id` dimension names System Overview's null user bucket \"System\". Readers that still use only the global default (noted, not carried): objectui's `DatasetReportRenderer` and `ObjectChart`, and the flat table, which draws a null dimension value as `—`."
}
}
},
Expand Down
2 changes: 1 addition & 1 deletion packages/spec/liveness/state-counts/dataset.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ committed anywhere: `check:liveness` sums the shards when it reads them.

| Type | live | exp | elsewhere | dead | planned | classified |
|---|---|---|---|---|---|---|
| `dataset` | 27 | 0 | 0 | 0 | 1 | 28 |
| `dataset` | 28 | 0 | 0 | 0 | 0 | 28 |
11 changes: 0 additions & 11 deletions packages/spec/src/system/metadata-form-zod-reconciliation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -365,17 +365,6 @@ const LEDGER: ReadonlyArray<OmitEntry | SubsetEntry> = [
key: 'conditionalFormatting',
why: "planned, not yet rendered — a field's cell formatting rules (`[{ condition, style }]`, the list view's own rule element) are validated at parse and their CEL is judged at the authoring doors (it parses, it reads only `value` and `record`), but no renderer styles a cell from them until objectui's cell renderers read them, after the spec publishes. A form control offered today would let an author set a style nothing paints",
},
// #25391 — a dataset dimension's null-bucket label lands spec-first the same
// way: `planned` in the liveness ledger, read by no chart renderer until the
// consumer half (ruling 6358843349 on #25330) ships. One list down, on the
// `dimensions` repeater rather than the root.
{
kind: 'omit',
type: 'dataset',
path: 'dimensions',
key: 'nullLabel',
why: "planned, not yet rendered — the label of a dimension's null bucket is validated at parse (an I18nLabel), but no chart renderer draws the bucket under it until objectui reads it over its own default, after the spec publishes. A repeater column offered today would let an author set a label nothing shows",
},
{
kind: 'omit',
type: 'view',
Expand Down
1 change: 1 addition & 0 deletions packages/spec/src/ui/dataset.form.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ export const datasetForm = defineForm({
{ field: 'field', label: 'Field' },
{ field: 'type', label: 'Type' },
{ field: 'dateGranularity', label: 'Date Granularity' },
{ field: 'nullLabel', label: 'Null Label' },
],
},
],
Expand Down
4 changes: 2 additions & 2 deletions scripts/sdui-manifest.record.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@
"a pin bump without regeneration goes RED there — that is the anti-rot half of the freshness gate.",
"Regenerate + re-record: pnpm objectui:build && node scripts/gen-sdui-manifest-node.mjs"
],
"objectuiSha": "5fef032b1ada5b86bad640637e7331e26ee5da6e",
"objectuiSha": "1d178e06ac5eda824af06f7d68eaccdcf38dcf63",
"source": "built-tree",
"modulesRoot": ".cache/objectui-5fef032b1ada/apps/console",
"modulesRoot": ".cache/objectui-1d178e06ac5e/apps/console",
"objectuiWorkspaceVersion": "18.0.0",
"generator": "scripts/gen-sdui-manifest-node.mjs",
"generatedAt": "2026-10-23",
Expand Down
Loading