fix(spec): reportForm offers a joined block's dataset as the ref:dataset picker, marked required (#21714) - #21819
Merged
objectstack-fleet[bot] merged 2 commits intoOct 5, 2026
Conversation
…set picker, marked required The "Joined blocks" repeater's `dataset` row declared only a label, so Studio's report inspector drew a free-text, unmarked cell for a key the joined arm of ReportSchema now refuses when absent (#21702). The row now declares `widget: 'ref:dataset'` (registered in objectui's WIDGETS at the console pin) and `required: true`, and a pin beside the refusal's own tests holds both. Claude-Session: https://claude.ai/code/session_01T9u38rswFp5Rw8DswRUReJ Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T9u38rswFp5Rw8DswRUReJ Co-authored-by: Claude <noreply@anthropic.com>
Contributor
📓 Docs Drift CheckNothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 1 changed package(s)), so this run has no opinion about the docs. What this run could not see
Coarse fallback — 138 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): |
objectstack-fleet
Bot
deleted the
claude/issue-21714-joined-block-dataset-widget
branch
October 5, 2026 06:05
This was referenced Oct 5, 2026
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 #21714
Clause-②: no
Claim
5987856715(the card's Change, unblocked by the director seat5987403710). InreportForm, the "Joined blocks" repeater'sdatasetrow now declareswidget: 'ref:dataset'andrequired: true. Studio's report inspector now draws a joined report's blockdatasetas the dataset picker, with the required marker, instead of a free-text cell with no marker. Since #21702 (PR #21712), the joined arm ofReportSchemarefuses a block that binds nodataset, atblocks.N.dataset. The old cell therefore let an author add a block that was invalid by construction.What changes
packages/spec/src/ui/report.form.ts, the "Joined blocks" repeater):{ field: 'dataset', label: 'Dataset' }becomes{ field: 'dataset', label: 'Dataset', widget: 'ref:dataset', required: true }, with a comment saying why. No other row, section or form changes.packages/spec/src/ui/report-joined-block-dataset.test.ts, beside the tests for the refusal itself): the row declareswidget: 'ref:dataset'andrequired: true. The pin reads the exportedreportForm, which is the output ofdefineForm's parse, so a form schema that stripped either key would also turn it red. The nested row is the same strict, recursiveFormFieldSchemathat already carrieswidgetandrequiredon top-level fields.@objectstack/specpatch(measurement below).JoinedReportBlockSchema.datasetstays optional on the block shape, and the joined arm's refusal is unchanged.defineFormreturnsFormViewParsed, soreportForm's exported type does not move. No objectui file.The renderer side, measured at the pin
.objectui-shaonmainis9dfaca654311. Every reading below comes fromgit show 9dfaca654311:PATHin an objectui clone, read-only.git merge-base --is-ancestor b508ac50d9 9dfaca654311exits 0, so objectui PR 11611's merge is in the pin. That reading is an exit 0 and therefore holds in a shallow clone too.9dfaca654311ref:datasetis registered inWIDGETSpackages/app-shell/src/views/metadata-admin/widgets.tsx:3035('ref:dataset': RefDatasetWidget). The widget is at:683, and it is declared'control'at:3144.RepeaterFieldresolves a row field's widget from the row specpackages/app-shell/src/views/metadata-admin/SchemaForm.tsx:2809: the grid cell callsresolveFieldWidget({ name: s.field, schema: sub, fieldSpec: s, widgetContext }). The card layout passesfieldSpec={s}toFieldRow(:2884), which resolves through the same function (:1838).inferWidgetreturns an explicitfieldSpec.widgetfirst (:447), and a spec that pins a widget skips every name detector (:1031).requiredSchemaForm.tsx:2799(grid cell) and:2882(card row) both passrequired={Boolean(s.required)}. The grid header draws the marker at:2772. For a non-boolean field,FieldRowdraws the star (:1878,:1917) and passesaria-requiredthroughFieldControl(:1930).The joined
blocksrepeater declares nowidget, souseGridis false (SchemaForm.tsx:2643) and its rows render in the card layout, asFieldRows. Three things connect the inspector to this row:ReportDefaultInspector.tsx:374–:381feeds the dataset catalog aswidgetContext.datasets, and passes it to the spec form at:652.report-schema.ts:80–:126(getReportForm) prunes only top-level section fields, so theblocksrow specs reachSchemaFormunchanged.reportFormfrom@objectstack/spec/ui(report-schema.ts:25). objectstack's console build injects this tree's spec into that import (scripts/build-console.sh,OBJECTSTACK_SPEC_DIST).Browser check: not done. It is optional on the card. It would need objectui's console built at the pin with this tree's spec injected, plus a backend and a logged-in Studio session, which is heavy on a shared container. The static chain above is the evidence. The browser reading of the card's second pin is still open.
Reverse verification
The fix was committed first (
02307d05b6). Thennode scripts/ablation-replace.mjsreplaced the anchorwidget: 'ref:dataset', required: true }withwidget: 'ref:dataset' }inreport.form.ts: anchor count 1 to 0, blob8bc1038e3612tob0da741da01a. The test imports./report.formfrom source, so nodist/is in the resolution path.expect(row?.required).toBe(true)and the other 16 stay green.Tests 1 failed | 16 passed (17),AssertionError: expected undefined to be trueatreport-joined-block-dataset.test.ts:261. The direction is the usual one, red.8bc1038e3612) andgit diff HEADis empty.Regenerated artifacts
None needed.
pnpm --filter @objectstack/spec buildfollowed bycheck:generatedreports all 15 generated artifacts up to date. The only generated files built from form specs are the nine packages' i18n bundles, which carrylabelandhelpText.pnpm check:i18nreportsOK (9 package(s) — all bundles in sync, no undeclared authoring keys).Changeset measurement
@objectstack/spec'sfiles[]shipsdistandsrc/**/*.zod.ts.report.form.tsitself is not packed (npm pack --dry-run --json: 2068 entries, noreport.form.ts), but its bundled bytes are. After the build,widget: "ref:dataset", required: trueoccurs in 6 packed files:dist/ui/index.{js,mjs},dist/system/index.{js,mjs}and the twodist/browser/systemtwins. Positive control: an unchanged string of the same form,Additional dataset-bound blocks stacked into a single report, occurs in the same 6. The diff therefore publishes from@objectstack/spec, so it carries apatchchangeset and noskip-changeset.Tests and gates, at
d7c811ff38pnpm --filter @objectstack/spec test:Test Files 615 passed (615),Tests 18361 passed | 1 todo.pnpm --filter @objectstack/spec typecheckexits 0:tsc --noEmit,check:scripts-typecheckandcheck:test-typecheck(52 file(s) / 246 error(s) / 135 pinned signature(s) held). Pertsc --listFilesOnly, the edited test file is intsconfig.test.json's program (1 hit) and not intsconfig.json's.platform-objectstests that walkreportForm(repeater-row-properties,report-form-echo-decisions,dataset-panel-echo-decisions):3 passed,62 passed.node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderived 86 commands.--ranreconciliation:86 derived, 86 run, 0 NOT-MEASURED, 0 UNRUN.PREREQUISITE NOT MET) andcheck:type-check-debtran past my per-command timeout. All six were re-run after a full workspace build (turbo run build --concurrency=2, 72 of 72 tasks) and exit 0.check-changeset-no-major --base origin/mainexits 0. Its clause-② level axis is PR-scoped and not applicable on a local run; CI reads this body'sClause-②line.Acceptance notes
reportFormreaches Studio from the@objectstack/specthat the console bundles. Through objectstack's console build that is this tree, because of theOBJECTSTACK_SPEC_DISTinjection. A standalone objectui build uses its own lockfile's@objectstack/specand gets the row once a spec release carrying it lands there.datasethint (report.form.ts:39) still never renders in the inspector, which prunes that field and draws its own curated picker (FORM_FIELDS_OWNED_ELSEWHERE,report-schema.ts:33). That was already known on the card, and nothing here changes it.Generated by Claude Code