Repository navigation
Commit 9059082
Fixes #21714
Clause-②: no
Claim `5987856715` (the card's Change, unblocked by the director seat
`5987403710`). In `reportForm`, the "Joined blocks" repeater's `dataset`
row now declares `widget: 'ref:dataset'` and `required: true`. Studio's
report inspector now draws a joined report's block `dataset` as the
dataset picker, with the required marker, instead of a free-text cell
with no marker. Since #21702 (PR #21712), the joined arm of
`ReportSchema` refuses a block that binds no `dataset`, at
`blocks.N.dataset`. The old cell therefore let an author add a block
that was invalid by construction.
## What changes
- **The row** (`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.
- **One pin**
(`packages/spec/src/ui/report-joined-block-dataset.test.ts`, beside the
tests for the refusal itself): the row declares `widget: 'ref:dataset'`
and `required: true`. The pin reads the exported `reportForm`, which is
the output of `defineForm`'s parse, so a form schema that stripped
either key would also turn it red. The nested row is the same strict,
recursive `FormFieldSchema` that already carries `widget` and `required`
on top-level fields.
- **Changeset:** `@objectstack/spec` `patch` (measurement below).
- ⛔ No schema, accept-set, type or export change.
`JoinedReportBlockSchema.dataset` stays optional on the block shape, and
the joined arm's refusal is unchanged. `defineForm` returns
`FormViewParsed`, so `reportForm`'s exported type does not move. No
objectui file.
## The renderer side, measured at the pin
`.objectui-sha` on `main` is `9dfaca654311`. Every reading below comes
from `git show 9dfaca654311:PATH` in an objectui clone, read-only. `git
merge-base --is-ancestor b508ac50d9 9dfaca654311` exits 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.
| claim | where, at objectui `9dfaca654311` |
|:--|:--|
| `ref:dataset` is registered in `WIDGETS` |
`packages/app-shell/src/views/metadata-admin/widgets.tsx:3035`
(`'ref:dataset': RefDatasetWidget`). The widget is at `:683`, and it is
declared `'control'` at `:3144`. |
| `RepeaterField` resolves a row field's widget from the row spec |
`packages/app-shell/src/views/metadata-admin/SchemaForm.tsx:2809`: the
grid cell calls `resolveFieldWidget({ name: s.field, schema: sub,
fieldSpec: s, widgetContext })`. The card layout passes `fieldSpec={s}`
to `FieldRow` (`:2884`), which resolves through the same function
(`:1838`). `inferWidget` returns an explicit `fieldSpec.widget` first
(`:447`), and a spec that pins a widget skips every name detector
(`:1031`). |
| it passes the row field's `required` | `SchemaForm.tsx:2799` (grid
cell) and `:2882` (card row) both pass `required={Boolean(s.required)}`.
The grid header draws the marker at `:2772`. For a non-boolean field,
`FieldRow` draws the star (`:1878`, `:1917`) and passes `aria-required`
through `FieldControl` (`:1930`). |
The joined `blocks` repeater declares no `widget`, so `useGrid` is false
(`SchemaForm.tsx:2643`) and its rows render in the card layout, as
`FieldRow`s. Three things connect the inspector to this row:
- `ReportDefaultInspector.tsx:374`–`:381` feeds the dataset catalog as
`widgetContext.datasets`, and passes it to the spec form at `:652`.
- `report-schema.ts:80`–`:126` (`getReportForm`) prunes only top-level
section fields, so the `blocks` row specs reach `SchemaForm` unchanged.
- The inspector reads `reportForm` from `@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`). Then `node
scripts/ablation-replace.mjs` replaced the anchor `widget:
'ref:dataset', required: true }` with `widget: 'ref:dataset' }` in
`report.form.ts`: anchor count 1 to 0, blob `8bc1038e3612` to
`b0da741da01a`. The test imports `./report.form` from source, so no
`dist/` is in the resolution path.
- **Predicted:** the new pin turns red at
`expect(row?.required).toBe(true)` and the other 16 stay green.
- **Observed:** `Tests 1 failed | 16 passed (17)`, `AssertionError:
expected undefined to be true` at
`report-joined-block-dataset.test.ts:261`. The direction is the usual
one, red.
- **Restore** (the tool's own leg, plus a shell trap): the blob equals
HEAD (`8bc1038e3612`) and `git diff HEAD` is empty.
## Regenerated artifacts
None needed. `pnpm --filter @objectstack/spec build` followed by
`check:generated` reports all 15 generated artifacts up to date. The
only generated files built from form specs are the nine packages' i18n
bundles, which carry `label` and `helpText`. `pnpm check:i18n` reports
`OK (9 package(s) — all bundles in sync, no undeclared authoring keys)`.
## Changeset measurement
`@objectstack/spec`'s `files[]` ships `dist` and `src/**/*.zod.ts`.
`report.form.ts` itself is not packed (`npm pack --dry-run --json`: 2068
entries, no `report.form.ts`), but its bundled bytes are. After the
build, `widget: "ref:dataset", required: true` occurs in 6 packed files:
`dist/ui/index.{js,mjs}`, `dist/system/index.{js,mjs}` and the two
`dist/browser/system` twins. 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 a `patch` changeset and no
`skip-changeset`.
## Tests and gates, at `d7c811ff38`
- `pnpm --filter @objectstack/spec test`: `Test Files 615 passed (615)`,
`Tests 18361 passed | 1 todo`.
- `pnpm --filter @objectstack/spec typecheck` exits 0: `tsc --noEmit`,
`check:scripts-typecheck` and `check:test-typecheck` (`52 file(s) / 246
error(s) / 135 pinned signature(s) held`). Per `tsc --listFilesOnly`,
the edited test file is in `tsconfig.test.json`'s program (1 hit) and
not in `tsconfig.json`'s.
- The three `platform-objects` tests that walk `reportForm`
(`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/objectstack` derived 86 commands.
- Every exit code was recorded before any pipe.
- `--ran` reconciliation: `86 derived, 86 run, 0 NOT-MEASURED, 0 UNRUN`.
- Five gates first exited 3 (`PREREQUISITE NOT MET`) and
`check:type-check-debt` ran 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/main` exits 0. Its clause-②
level axis is PR-scoped and not applicable on a local run; CI reads this
body's `Clause-②` line.
## Acceptance notes
- `reportForm` reaches Studio from the `@objectstack/spec` that the
console bundles. Through objectstack's console build that is this tree,
because of the `OBJECTSTACK_SPEC_DIST` injection. A standalone objectui
build uses its own lockfile's `@objectstack/spec` and gets the row once
a spec release carrying it lands there.
- The top-level `dataset` hint (`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](https://claude.ai/code/session_01T9u38rswFp5Rw8DswRUReJ)_
---------
Co-authored-by: Claude <noreply@anthropic.com>
1 parent a3ffc45 commit 9059082
3 files changed
Lines changed: 33 additions & 1 deletion
File tree
- .changeset
- packages/spec/src/ui
Lines changed: 11 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
27 | 27 | | |
28 | 28 | | |
29 | 29 | | |
| 30 | + | |
30 | 31 | | |
31 | 32 | | |
32 | 33 | | |
| |||
245 | 246 | | |
246 | 247 | | |
247 | 248 | | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
248 | 265 | | |
249 | 266 | | |
250 | 267 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
82 | 82 | | |
83 | 83 | | |
84 | 84 | | |
85 | | - | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
86 | 90 | | |
87 | 91 | | |
88 | 92 | | |
| |||
0 commit comments