Repository navigation
Commit 16eefc6
Fixes #21315
Clause-②: no
## What changed
An `object-master-detail-form` detail entry has two describes for keys
the renderer can fill in when they are omitted: `sortField` and
`amountField`. Both now state what happens on each of the renderer's
paths. The reference page that lifts them
(`content/docs/references/ui/component.mdx`) is regenerated by
`check:generated --fix`. No schema accepts or refuses anything new.
There is no shape, default or nullability change, and no objectui edit.
The model is PR #21307, which did the same for `inlineMode` and
`formFields`.
`sortField`, before:
> Child field holding the line sort position, stamped on drag-reorder
(derived from a `position` / `sort_order` / … field when omitted)
after:
> Child field holding the line sort position, stamped on drag-reorder.
When omitted it is the child object's first field named `position`,
`sort_order`, `sequence`, `line_no`, `line_number` or `sort`, if it has
one — except on an entry that names `relationshipField` and at least one
column and gives every column a `type`. The renderer keeps that entry
exactly as authored: nothing is derived, the grid stamps no line
position, and a drag-reorder is not saved
`amountField`, before:
> Numeric child column summed for the running total
after:
> Numeric child column summed for the running total and the `totalField`
rollup. When omitted it is picked from the grid's number and currency
columns: a computed one, else one named `amount`, `total`, `subtotal`,
`line_total`, `line_amount` or `net_amount`, else the last currency
column, else the last numeric one — except on an entry that names
`relationshipField` and at least one column and gives every column a
`type`. The renderer keeps that entry exactly as authored, so nothing is
picked. With no `amountField` authored or picked, the sums read a child
column named `amount`, and the grid shows a running total only when
`totalField` is set
## What the renderer does (objectui at the `.objectui-sha` pin
`31971ff1e28f`, read with `git show`)
`packages/plugin-form/src/MasterDetailForm.tsx`:
- 927–929, `needsDerive`: an entry needs resolution when it has no
`relationshipField`, no columns, or a column without a `type`. At 958,
when no entry in the block needs it, every entry is kept as authored.
- 965–967, the fast path: `relationshipField` is set and every column
(at least one) has a `type`. The entry is returned unchanged (`{
...entry, status: 'ready' }`). An omitted `sortField` stays undefined,
and 862 passes `sort_field: d.sortField`, which is undefined. An omitted
`amountField` also stays undefined.
- 1032–1038: every other entry that loads its child schema goes through
`deriveDetail`. Then `amountField = d.amountField ??
derived.amountField` and `sortField = d.sortField ?? derived.sortField`.
- 1048–1055, the hydrate path: `relationshipField` is set and columns
are present, but some have no `type`. The config becomes `{ ...d,
columns: derived.columns, amountField, sortField }`. So `formFields` and
`inlineMode` are kept as authored here, but `sortField` and
`amountField` ARE derived. The comment at 1040–1047 gives the reason
(objectui#11144).
- 1056–1069, the derived path: everything is derived, with `amountField`
and `sortField` at 1065–1066.
`packages/plugin-form/src/deriveMasterDetail.ts`:
- 540 with 55: the derived `sortField` is the first child field whose
name is one of `position`, `sort_order`, `sequence`, `line_no`,
`line_number` or `sort`.
- 534 with 476–495: the derived `amountField` is
`pickAmountField(columns)`. That is a computed number or currency
column, else one named in `AMOUNT_LIKE_FIELDS`, else the last currency
column, else the last numeric column.
`packages/fields/src/widgets/GridField.tsx` 726–737: the grid stamps
`row[sortField] = index` only when `sort_field` is set. Its comment
reads "Without it, reorder is order-of-entry."
When no `amountField` is authored or picked (`MasterDetailForm.tsx`):
- 728: the document subtotal sums `e.config.amountField || 'amount'`.
- 861: the grid's `total_field` is `d.amountField || (d.totalField ?
'amount' : undefined)`, unless the tax stack below it takes over.
- 1549–1550: the `totalField` rollup writes `sumRows(rows, d.amountField
|| 'amount')`.
So the path split is not #21284's. `formFields` and `inlineMode` are
kept as authored on both the fast path and the hydrate path, which is an
entry that names `relationshipField` and at least one column.
`sortField` and `amountField` are kept as authored only on the fast
path, where every column also has a `type`. Each describe states its own
split.
## Why `amountField` changes too
The triage direction put it in this edit. Its old text made no false
claim, but it said nothing about omission, and omission changes the
result by path. On the fast path nothing is picked, and the sums read a
column named `amount`. From 1549–1550 (a reading, not a run): take a
fully typed entry whose line-total column is `line_total`, with
`totalField` set and `amountField` omitted. It writes 0 into the
parent's `totalField`, because `sumRows` counts a missing value as 0.
The derived path would have picked `line_total`. An author cannot know
to write the key on the fast path unless the describe says so.
## Every member of `masterDetailDetailEntry()`, read for the same gap
- `childObject`: required, and nothing derives it. Holds.
- `relationshipField`: "auto-detected … when omitted". The fast and
hydrate paths both need the key, so an entry without it always takes the
derived path, and `findRelationshipField` (`deriveMasterDetail.ts`
141–154) runs there. That function takes the `master_detail` field that
references the parent, else a `lookup`. Holds.
- `columns`: "derived from the child object when omitted". The fast and
hydrate paths both need columns, so an entry without them always reaches
`deriveColumns`. A column given only its `name` has no `type`, so the
entry is hydrated. Holds.
- `formFields` and `inlineMode`: #21284's text, re-read at the pin. They
are kept as authored at 967 and 1048–1055, and derived at 1063–1064. The
offer conditions are at 847 and 850. Holds.
- `amountField` and `sortField`: changed above.
- `totalField`: nothing derives it, and its describe says nothing about
omission. The sum it receives is the one the `amountField` describe now
explains. Holds.
- `title`, `minRows`, `maxRows` and `addLabel`: passed through or given
a default label (755, 863–865). None of their describes claims an
omission behaviour. Holds.
- The factory's TSDoc calls `sortField` "the line-position field the
grid stamps on drag-reorder" and claims no derivation. Holds, not
edited.
## The `sortField` retirement waits on the pin bump
objectui `main` retired the authored `sortField` at `0a3e5409f`
(objectui#11376), after this pin. `git merge-base --is-ancestor
31971ff 0a3e540` exits 0, so the pin is in that commit's history
and `0a3e5409f` is not in the pin's. At the pin, `sortField` is still
read (85, 862, 1038). This PR does not retire the key. The
`.objectui-sha` bump that crosses `0a3e5409f` owes the spec half: retire
`sortField` from this entry, with a tombstone and an ADR-0087 entry, in
the same landing. The `sortField` describe written here is what the
current pin does until then. For `amountField`, objectui `main`
(`d8edfe2576`) still returns the fast-path entry unchanged at 979, and
still sums `d.amountField || 'amount'` (740, 1564). So, as of that
commit, the bump owes no `amountField` change.
## Acceptance notes
- A form view's `subforms[]` entry (`FormViewSchema.subforms` in
`packages/spec/src/ui/view.zod.ts`) has the same `amountField` describe,
"Numeric child column summed for the running total". At the pin,
`ObjectForm.tsx` 340 and 394 send a form with `subforms` to
`MasterDetailForm` as `details`, so the same omission behaviour applies
there. That describe makes no false claim, so this is an observation,
not a filed defect. This PR keeps to its card's surface. Carrier: none.
## Verification (every reading below is on HEAD `e0b23743d5`)
- `pnpm --filter @objectstack/spec build`: `VERDICT command-exit 0`.
- `pnpm --filter @objectstack/spec check:generated`: the first run
reported `1 of 15 artifact(s) stale: content/docs/references/**`, and
every other artifact was current, including the authorable surface and
the JSON schemas. `--fix` regenerated that one and its re-check gave `✓
check:docs`. In the gate union it exits 0.
- `pnpm --filter @objectstack/spec typecheck`: exit 0.
- `pnpm --filter @objectstack/spec exec vitest run --project local
--maxWorkers=2`: `Test Files 597 passed (597)`, `Tests 17487 passed | 1
todo (17488)`.
- `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack
--commands`, run with no paths: 102 commands. The sorted list is
identical to the dispatch's derivation at `97239c3c8a`. All were run,
with exit codes recorded. `--ran` answers `102 derived famil(ies)
accounted for — 101 run, 1 NOT-MEASURED (1 DERIVED from a recorded exit
3)`.
- Six gates first exited 3 because their prerequisite packages were not
built: `check:doc-formula-expressions`, `check:doc-security-posture`,
`check:skill-examples`, `check:docs-transcript-drift`,
`check:lean-entry-closure` and `check:dual-build-cjs-loads`. After
building that closure (`turbo run build` for formula, lint, client-react
and objectql, 34 tasks), the first five exit 0.
- NOT MEASURED: `pnpm check:dual-build-cjs-loads`. It needs every
workspace package's `dist` (hono, account, setup, studio, client and
more), which needs a full-repo build. As a narrower check, all 19
`require` entry points of `@objectstack/spec` load. CI runs the full
gate.
- eslint, narrowed. Of the diff's three files, only `component.zod.ts`
is in eslint's population: for the `.md` and `.mdx` files, eslint
answers "File ignored because no matching configuration was supplied".
`--format json` reports 0 errors and 0 warnings for that file. Its
resolved config has `parserOptions` without `project`, so linting is not
type-aware and this diff cannot change the verdict for any untouched
file. The full `pnpm lint` runs in CI.
- `main` was not merged in. Since the base `97239c3c8a`, `origin/main`
has gained three commits, and none of them touches `packages/spec` or
the reference page.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01UtnxvdiN376GF3sgXwAw4d)_
---------
Co-authored-by: Claude <noreply@anthropic.com>
1 parent 23365ea commit 16eefc6
3 files changed
Lines changed: 16 additions & 4 deletions
File tree
- .changeset
- content/docs/references/ui
- packages/spec/src/ui
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
978 | 978 | | |
979 | 979 | | |
980 | 980 | | |
981 | | - | |
982 | | - | |
| 981 | + | |
| 982 | + | |
983 | 983 | | |
984 | 984 | | |
985 | 985 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
5033 | 5033 | | |
5034 | 5034 | | |
5035 | 5035 | | |
5036 | | - | |
5037 | | - | |
| 5036 | + | |
| 5037 | + | |
5038 | 5038 | | |
5039 | 5039 | | |
5040 | 5040 | | |
| |||
0 commit comments