Repository navigation
fix(app-shell): a blank dataset Field box writes no field key; a field-less dimension holds Save (objectui#11402) - #11408
Merged
objectstack-fleet[bot] merged 6 commits intoOct 2, 2026
Conversation
… key (objectui#11402) Red first, against the unchanged inspector: new dimension and measure rows are seeded with `field: ''`, a blank-Field measure saves `field: ''`, and a field-less dimension is reported on no blocking-issue channel. Claude-Session: https://claude.ai/code/session_01JG2jy8a9su7ia4Hx7zxv42 Co-authored-by: Claude <[email protected]>
…eld-less dimension holds Save (objectui#11402) New dimension and measure rows are seeded without a `field` key, and every write of the Field box goes through `writeRowField`, which removes the key on a blank value instead of storing `''`. A measure therefore saves the one spelling the spec has for "no field" (count(*)). A dimension's `field` is required, so a field-less (or stored `''`) dimension is reported on the inspector's blocking-issue channel, the one the host's Save gate, autosave timer and shortcut already read (objectui#4527 / objectui#6900); its Field label carries the designer's required marker (objectui#10948). No `id` default is written on the author's behalf. Claude-Session: https://claude.ai/code/session_01JG2jy8a9su7ia4Hx7zxv42 Co-authored-by: Claude <[email protected]>
…r than the spec (objectui#11402) The host keeps a verdict the server also returns advisory, because a client gate stricter than the server would wedge Save. The field-less dimension hold is safe on that count only while `DatasetSchema` itself refuses a dimension without a `field`; this pins that, and goes red if the key ever turns optional. Claude-Session: https://claude.ai/code/session_01JG2jy8a9su7ia4Hx7zxv42 Co-authored-by: Claude <[email protected]>
…jectui#11402) Claude-Session: https://claude.ai/code/session_01JG2jy8a9su7ia4Hx7zxv42 Co-authored-by: Claude <[email protected]>
…m compiles (objectui#11402)
A field-less object literal shares no key with `{ field?: string }`, so left
to inference it reads as a weak type and `tsc -p tsconfig.test.json` (the
second leg of the package's `type-check`) refused its other keys with TS2353.
The rows are now typed as the inspector's own row shape; the assertions are
unchanged.
Claude-Session: https://claude.ai/code/session_01JG2jy8a9su7ia4Hx7zxv42
Co-authored-by: Claude <[email protected]>
…ld label only while the box is blank (objectui#11402) The ruling asks that a dimension with a blank Field box shows as incomplete. A marker on every dimension's Field label said "required" on complete rows too, and it changed the label's text for every row, which turned the `inspectorStrings.i18n-10696` lit control red: it finds the Field combo by the label's exact text on a dimension that already has a field. The marker now renders while the box is blank, so the row the Save hold is about is the one shown as incomplete, and a complete row keeps the plain label. The pin asserts both states. Claude-Session: https://claude.ai/code/session_01JG2jy8a9su7ia4Hx7zxv42 Co-authored-by: Claude <[email protected]>
Contributor
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
objectstack-fleet
Bot
deleted the
claude/issue-11402-dataset-blank-field
branch
October 2, 2026 00:33
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #11402
Clause-②: no
What changed
DatasetDefaultInspectorno longer writesfield: ''for a dataset row whose Field box is blank.fieldkey. "Add dimension" seeds{ name: '', type: 'string' }and "Add measure" seeds{ name: '', aggregate: 'sum' }. Neither writes aniddefault or any other field on the author's behalf.writeRowField, which rebuilds the row withoutfieldwhen the value is blank. It does not store''orfield: undefined, so the held draft and the saved body agree. Acountmeasure with no field saves the one spelling the spec has for count(*): the key is absent.fieldis absent or blank on itsonBlockingIssuesChangechannel. That count includes a storedfield: ''. The host already reads this channel for the default-inspector family (objectui#4527, objectui#6900): it holds the Save button, the autosave timer and the shortcut until a field is picked. While the box is blank, that row's Field label also shows the designer's required marker (RequiredMarker, objectui#10948). A complete row shows the plain label. Measures are not held: a measure'sfieldis optional, and whether its aggregate may go without one is the spec's verdict at save.Rulings → where each lands (triage
5941542449)fieldkey.onAddseeds; pinsseeds a new measure row …,seeds a new dimension row …field.writeRowFieldviapickMeasureField; pin⭐ a measure added and saved with a blank Field box carries no field, and the saved dataset parses under DatasetSchema''.a blank-Field dimension is held as incompleteiddefault.toStrictEqualWhich "incomplete" mechanism this reuses, and why it is licensed here
I measured what this inspector family has for "incomplete": the
onBlockingIssuesChangeSave gate, the staticRequiredMarker, and the host's live Zod pass. The Zod pass does not exist fordataset:clientValidation.tsregisters no loader for it. Nothing new was invented. The hold reuses the Save gate, and the marker reusesRequiredMarker.A reviewer should weigh one point. The host's note above
blockingReportinResourceEditPagedescribes this channel as carrying faults the server does not catch, and it keeps schema verdicts advisory. The reason is that a client gate stricter than the server would wedge Save on a body the server accepts.@objectstack/spec17.5.0,DatasetDimensionSchema.fieldis a required string) refuses it as well. So the hold is never stricter than the spec. A new pin,the hold is never stricter than the spec …, assertsDatasetSchemarefuses that dimension atdimensions.0.fieldand accepts it once a field is picked. It goes red if the key ever turns optional.field: ''. This is the one row the hold refuses that 17.5.0 accepts. The ruling covers it ("not saved with''"), and the Field box on screen repairs it, so it cannot wedge.''seed alone would send every freshly added dimension to the server without a field. Autosave would then draw a 422 on every "Add dimension" until a field is picked. This is inferred, not measured against a live server:DatasetDimensionSchema.fieldis required on 17.5.0, anddatasethas no client validator.Inherited vs re-run (this run resumed a killed one)
93f85530(the pin) and200e91d8(the fix). I did not take either on trust.7728c67cbytes and the pin file unchanged, 8 tests failed and 1 passed (9 total) at44fa23cf. Each failure is on the asserted fact: the seeds carryfield: '', the held row has afieldkey, nothing is reported on the blocking channel, andwriteRowFieldis absent. The one pass is the spec control, which asserts the spec rather than the inspector. The restore usedgit checkout HEAD -- PATH. I proved it by comparing hashes: the file's blob equals the HEAD blob725de67b, andgit diff HEADis empty.93c6598c: the inherited pin did not compile undertsc -p tsconfig.test.json, the second leg of the package'stype-check. A field-less literal passed towriteRowFieldread as a weak type and drew TS2353. The rows are now typed.44fa23cf: the inherited fix put the marker on every dimension's Field label. That changed the label text on complete rows and turnedinspectorStrings.i18n-10696's lit control red, because it finds the Field combo by the label's exact text. The marker now renders only while the box is blank. That is also closer to the ruling's "shows as incomplete" than a static "required" mark on complete rows.e98eb867: the spec-control pin described above.c5648ec6).Tests and gates — all read at
44fa23cfNode v22.22.2 from
/opt/nvm(the container's v22.22.0 is refused by the engine-strict install).pnpm --filter @object-ui/app-shell type-check && pnpm --filter @object-ui/app-shell buildexited 0. Bothtsclegs echoed, and the build printed✓ dist completeness: 1 package(s) complete.tsc -p tsconfig.test.json --listFileslists the pin file. I built the dependency closure first withturbo run build --filter=@object-ui/app-shell... --concurrency=2: 29 of 29 tasks succeeded.pnpm exec vitest run --maxWorkers=2over every suite inpackages/app-shell/src/views/metadata-admin/inspectors/, plus the dataset readerspreviews/DatasetPreview*,DatasetPreview.dimensionLabels-8187anddataPreviews.i18n-10862-s3:Test Files 125 passed (125),Tests 1727 passed | 1 skipped (1728). The skip is an existingskipIfon a spec version inflow-node-config.spec-reconciliation.pnpm check:control-bytespnpm check:new-line-citations(VERDICT … 0 new citation(s))node scripts/check-changeset-presence.mjspnpm changeset:checkcheck:vi-mock-specifiers,check:vi-mock-inheritandcheck:vi-mock-override-shape(the pin mocks./useDatasetFields)check:test-path-rootscheck:i18n-keyscheck:changeset-claimscheck:pending-changeset-literalseslint .under the rooteslint.config.js. Both changed files are linted, not ignored.--format jsonreports 2 files, 0 errors and 4 warnings. One warning is new:react-refresh/only-export-componentson thewriteRowFieldexport, the same rule the file's existingobjectChangePatchexport already draws.lint.ymlsets no--max-warnings.eslint.config.jsenables no type-aware linting, and no rule undereslint-rules/reads the disk. So this diff cannot move a verdict on an untouched file.check-governed-queue-guard.mjs --testreportsNOT GOVERNED.Readers of these rows (checked for an absent
field)DatasetPreviewreadsString(x?.field ?? '')and skips an empty one.ReportView's drill fallback readsdimDef?.field || dim.missingRelationshiptakesfield: string | undefined.useDatasetCatalognever readsfield. None of them breaks.Acceptance notes (observations, not filed)
field: ''(written by this inspector before this change) is re-sent unchanged unless the author edits it. The card records that acountmeasure carrying it answered 500 on the ObjectQL strategy (the objectstack dev's measurement, not re-taken here). Under feat(spec)!: an analytics dataset dimension's and measure's field is a column reference (#21220) objectstack#21240 it is refused atmeasures.N.field. The combo has no "clear" affordance, so the in-UI repair is the Source tab. Repairing stored data was not asked for here.InspectorComboFieldoffers no way to clear a picked value, so the inspector has no UI path to clear a measure's Field box back to count(*).writeRowFieldcovers a blank value if one ever arrives.ResourceEditPage's Save-button comment andinspectorBlockingnote name CEL faults and the objectui#6900 refusal as what the channel carries. The dataset dimension hold is now a third kind.Generated by Claude Code