You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 138dacf
Browse filesBrowse the repository at this point in the historyBrowse files
docs(spec): relatedList describe states each value's role, not the record page's tabs (#29799)
domain:spec#3 · os-support-ai · session_01WGufzgh6TyUUCFMAfapZqb
<!-- claude-projects-attribution: {"github_login":"os-support-ai"} -->
_Requested by **Yibo Wang** · [project
thread](https://claude.ai/code/project/chan_01BVUyyPie8gHwoPM18iABwB?thread=cmsg_01BVUyyPie8gHwoPM18iABwBBBNTDAYRKaoWkKn1quxAC4)_Fixes#28888
Ruling-ref: 6568787922
Clause-②: no
Metadata-protocol: none
Skills-docs: none — this PR moves no skill text. One skill sentence
still names the old chrome for the same placement,
`skills/objectstack-data/rules/relationships.md:345-348` ("in the
Details tab, no tab of its own", "a side card"); it is out of this
card's file surface (spec only) and is reported to the seat for its own
card.
## 维护者速读
- 改了什么:`relatedList` 字段的说明文字(`.describe()` 与上方 TSDoc)不再提任何标签页或栏位:`false`
= 父记录页不显示该子对象;`true` 或不写 = 记录页上的一个辅助面板;`'primary'` = 记录详情主体里的一张通栏表格。版式只引
ADR-0085,不复述。
- 为什么改:裁决 6568787922(字母
B)。旧文字描述的共用「Related」标签和「Details」标签在现在的记录页上已不存在,AI 作者照着读会把用户引向不存在的标签。
- 风险与回滚:只改文字,不动键、默认值、可接受值;`@objectstack/spec` patch。生成的参考页(field /
object / migration 三页的同一行)随之重生成。回滚即还原本 PR。
- 先看:`packages/spec/src/data/field.zod.ts`(describe 与
TSDoc)、`packages/spec/src/data/record-page-describes.test.ts`(原来钉着旧措辞的用例改钉新措辞,并加一条「不提
tab / column / Related / Details / side card」)。
- 两分钟验证:`pnpm --filter @objectstack/spec exec vitest run
src/data/record-page-describes.test.ts` 全绿;换回 `origin/main` 的
`field.zod.ts` 再跑,2 条红。
- 需要你做的:无,席位落地。
## What changes
`packages/spec/src/data/field.zod.ts`, the `relatedList` `.describe()`
and the TSDoc block above it. Every other clause keeps its meaning: the
master_detail + inlineEdit `'primary'` default, `relatedLists` ordering,
and the derived-list sort sentences are unchanged.
**Before** (the value clauses):
> false = suppress; true/absent = shown as a supporting list, a side
card in the record's related column (the shared "Related" tab on the
tabbed page); 'primary' = part of the record's main content, drawn in
the record's body (the details column, after its first field section) as
a full-width table (inside the Details tab on the tabbed page, with no
tab of its own). Absent on a master_detail field that sets inlineEdit,
the list is 'primary' by default; write true to keep it a side card.
'primary' places a list and does not order it: the parent object's
relatedLists orders the body tables and the side cards (ADR-0085).
**After:**
> false = the child object is not shown on the parent record page;
true/absent = shown as an auxiliary panel on the record page; 'primary'
= part of the record's main content, shown as a full-width table in the
record's detail body. Absent on a master_detail field that sets
inlineEdit, the list is 'primary' by default; write true to keep it an
auxiliary panel. 'primary' places a list and does not order it: the
parent object's relatedLists orders the full-width tables and the
auxiliary panels. The page layout is defined by ADR-0085.
The TSDoc drops "related column", "shared \"Related\" tab", "details
column", "Details tab ... no tab of its own" and "side card" the same
way, and adds one line saying the layout is ADR-0085's to define. It
keeps the sentence that a field group's `relatedList` can place a
primary list among the field sections, since that is a real placement
key, not page chrome.
Generated: `content/docs/references/data/field.mdx`, `data/object.mdx`,
`system/migration.mdx` (the same `relatedList` row; `check:generated
--fix`, then `check:generated` clean).
## Why
Ruling 6568787922, letter B: the describe states the field's meaning,
not the page's chrome, so the next layout card under #26676 does not
make it wrong again.
## Verification
- Acceptance: `git grep -n 'Related tab\|Details tab' --
packages/spec/src/data/field.zod.ts` is empty at head.
- `record-page-describes.test.ts`: the `[#27312]` case that pinned "the
details column, after its first field section" and "inside the Details
tab on the tabbed page, with no tab of its own" now pins the three new
value clauses and the ADR-0085 sentence; a new case refuses `tab(s)`,
`column`, `"Related"`, `Details` and `side card` in the describe. Red
first: with `origin/main`'s `field.zod.ts`, `Tests 2 failed | 9 passed
(11)`; restored, green.
- `pnpm exec vitest run src/data/record-page-describes.test.ts
src/data/field.test.ts` under os-verify-lock: `Tests 303 passed (303)`,
VERDICT command-exit 0.
- Derived gates: `dispatch-gates --commands` derived 102 families at
`9a801adf`; 95 exit 0, 0 red, 7 NOT MEASURED (exit 3, prerequisite not
met: `check-plugin-teardown-shape --self-test` needs its pinned fixture
commit in this shallow clone; `check:doc-formula-expressions`,
`check:doc-security-posture`, `check:skill-examples`,
`check:docs-transcript-drift`, `check:dual-build-cjs-loads`,
`check:lean-entry-closure` need a full build). `dispatch-gates --ran`:
102 accounted, 0 unrun. CI covers the 7.
## Acceptance notes
Grep of `packages/spec/src/**` for the record page's "Related" /
"Details" tab and its columns, other than this describe:
- `src/data/record-page-describes.test.ts` (the old pin, changed here).
- `src/data/object.zod.ts:1251,1273` and `src/data/object.form.ts:113`:
`fieldGroups[i].relatedList` says "details column", "body table" and
"side card". That is a different key (a field group's placement), so per
the order it is reported, not fixed.
-
`src/migrations/entries/semantic/18.field-group-related-list-side-card-refused.ts`:
migration history text, left as it is.
## Human Minimum
None; the seat lands it.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01WGufzgh6TyUUCFMAfapZqb
Co-authored-by: Claude <noreply@anthropic.com>
docs(spec): the `relatedList` field description says what each value does on the parent record page (`false` hidden, `true`/absent an auxiliary panel, `'primary'` a full-width table in the detail body) and points at ADR-0085 for the layout, instead of naming the retired "Related" and "Details" tabs and the page's columns
Copy file name to clipboardExpand all lines: content/docs/references/data/field.mdx
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -85,7 +85,7 @@ const result = CurrencyConfigSchema.parse(data);
85
85
|**inlineTitle**|`string`| optional | Title for the inline master-detail grid |
86
86
|**inlineColumns**|`{ name: string; label?: string; type?: Enum<'text' \| 'number' \| 'currency' \| 'date' \| 'datetime' \| 'time' \| 'select' \| 'lookup' \| 'file'>; width?: number; … }[]`| optional | Explicit columns for the inline grid (derived from the child object when omitted). Each entry is a strict, name-keyed column (`{ name, label?, type?, … }` — objectui GridColumn); identity-only entries (`{ name }`) hydrate everything else from the child object's fields. Unknown keys and the retired `field` spelling are refused at parse. |
87
87
|**inlineAmountField**|`string`| optional | Numeric child field summed for the inline grid total |
88
-
| **relatedList** | `boolean \| 'primary'` | optional | Show this child collection as a related list on the parent's detail page (read-side mirror of inlineEdit). false = suppress; true/absent = shown as a supporting list, a side card in the record's related column (the shared "Related" tab on the tabbed page); 'primary' = part of the record's main content, drawn in the record's body (the details column, after its first field section) as a full-width table (inside the Details tab on the tabbed page, with no tab of its own). Absent on a master_detail field that sets inlineEdit, the list is 'primary' by default; write true to keep it a side card. 'primary' places a list and does not order it: the parent object's relatedLists orders the body tables and the side cards (ADR-0085). A derived related list (relatedList: 'primary') inherits its row order from the child object's DEFAULT list view sort — the isDefault view, or the first declared list item when none is marked default; wire spelling is sort=`<field>` / sort=-`<field>`, never $orderby. A child object with no list-view sort emits no ordering parameter, and rows fall back to record-id order. |
88
+
| **relatedList** | `boolean \| 'primary'` | optional | Show this child collection as a related list on the parent's detail page (read-side mirror of inlineEdit). false = the child object is not shown on the parent record page; true/absent = shown as an auxiliary panel on the record page; 'primary' = part of the record's main content, shown as a full-width table in the record's detail body. Absent on a master_detail field that sets inlineEdit, the list is 'primary' by default; write true to keep it an auxiliary panel. 'primary' places a list and does not order it: the parent object's relatedLists orders the full-width tables and the auxiliary panels. The page layout is defined by ADR-0085. A derived related list (relatedList: 'primary') inherits its row order from the child object's DEFAULT list view sort — the isDefault view, or the first declared list item when none is marked default; wire spelling is sort=`<field>` / sort=-`<field>`, never $orderby. A child object with no list-view sort emits no ordering parameter, and rows fall back to record-id order. |
89
89
|**relatedListTitle**|`string`| optional | Title for the detail-page related list |
90
90
|**relatedListColumns**|`string[]`| optional | Explicit columns for the detail-page related list, as child field names (e.g. ['name', 'status']); derived from the child object (highlightFields → field walk) when omitted. Strings only — labels, cell types and formatting always derive from the child object's field definitions; column objects are refused at parse. |
91
91
|**relatedListFilter**|`any`| optional | Declarative default filter for the detail-page related list: AND-composed with the parent-relationship condition `{ [referenceField]: parentId }` — an authored constraint, never a user-editable suggestion. The related-list tab badge count honors the same composed filter, so counts match the visible rows. Canonical Query-DSL FilterCondition (the same dialect as a query `where`), e.g. `{ status: { $ne: 'deleted' } }` to hide soft-deleted children. |
Copy file name to clipboardExpand all lines: content/docs/references/data/object.mdx
+2-2Lines changed: 2 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -254,7 +254,7 @@ const result = ApiMethod.parse(data);
254
254
|**inlineTitle**|`string`| optional | Title for the inline master-detail grid |
255
255
|**inlineColumns**|`{ name: string; label?: string; type?: Enum<'text' \| 'number' \| 'currency' \| 'date' \| 'datetime' \| 'time' \| 'select' \| 'lookup' \| 'file'>; width?: number; … }[]`| optional | Explicit columns for the inline grid (derived from the child object when omitted). Each entry is a strict, name-keyed column (`{ name, label?, type?, … }` — objectui GridColumn); identity-only entries (`{ name }`) hydrate everything else from the child object's fields. Unknown keys and the retired `field` spelling are refused at parse. |
256
256
|**inlineAmountField**|`string`| optional | Numeric child field summed for the inline grid total |
257
-
| **relatedList** | `boolean \| 'primary'` | optional | Show this child collection as a related list on the parent's detail page (read-side mirror of inlineEdit). false = suppress; true/absent = shown as a supporting list, a side card in the record's related column (the shared "Related" tab on the tabbed page); 'primary' = part of the record's main content, drawn in the record's body (the details column, after its first field section) as a full-width table (inside the Details tab on the tabbed page, with no tab of its own). Absent on a master_detail field that sets inlineEdit, the list is 'primary' by default; write true to keep it a side card. 'primary' places a list and does not order it: the parent object's relatedLists orders the body tables and the side cards (ADR-0085). A derived related list (relatedList: 'primary') inherits its row order from the child object's DEFAULT list view sort — the isDefault view, or the first declared list item when none is marked default; wire spelling is sort=`<field>` / sort=-`<field>`, never $orderby. A child object with no list-view sort emits no ordering parameter, and rows fall back to record-id order. |
257
+
| **relatedList** | `boolean \| 'primary'` | optional | Show this child collection as a related list on the parent's detail page (read-side mirror of inlineEdit). false = the child object is not shown on the parent record page; true/absent = shown as an auxiliary panel on the record page; 'primary' = part of the record's main content, shown as a full-width table in the record's detail body. Absent on a master_detail field that sets inlineEdit, the list is 'primary' by default; write true to keep it an auxiliary panel. 'primary' places a list and does not order it: the parent object's relatedLists orders the full-width tables and the auxiliary panels. The page layout is defined by ADR-0085. A derived related list (relatedList: 'primary') inherits its row order from the child object's DEFAULT list view sort — the isDefault view, or the first declared list item when none is marked default; wire spelling is sort=`<field>` / sort=-`<field>`, never $orderby. A child object with no list-view sort emits no ordering parameter, and rows fall back to record-id order. |
258
258
|**relatedListTitle**|`string`| optional | Title for the detail-page related list |
259
259
|**relatedListColumns**|`string[]`| optional | Explicit columns for the detail-page related list, as child field names (e.g. ['name', 'status']); derived from the child object (highlightFields → field walk) when omitted. Strings only — labels, cell types and formatting always derive from the child object's field definitions; column objects are refused at parse. |
260
260
|**relatedListFilter**|`any`| optional | Declarative default filter for the detail-page related list: AND-composed with the parent-relationship condition `{ [referenceField]: parentId }` — an authored constraint, never a user-editable suggestion. The related-list tab badge count honors the same composed filter, so counts match the visible rows. Canonical Query-DSL FilterCondition (the same dialect as a query `where`), e.g. `{ status: { $ne: 'deleted' } }` to hide soft-deleted children. |
@@ -625,7 +625,7 @@ const result = ApiMethod.parse(data);
625
625
|**inlineTitle**|`string`| optional | Title for the inline master-detail grid |
626
626
|**inlineColumns**|`{ name: string; label?: string; type?: Enum<'text' \| 'number' \| 'currency' \| 'date' \| 'datetime' \| 'time' \| 'select' \| 'lookup' \| 'file'>; width?: number; … }[]`| optional | Explicit columns for the inline grid (derived from the child object when omitted). Each entry is a strict, name-keyed column (`{ name, label?, type?, … }` — objectui GridColumn); identity-only entries (`{ name }`) hydrate everything else from the child object's fields. Unknown keys and the retired `field` spelling are refused at parse. |
627
627
|**inlineAmountField**|`string`| optional | Numeric child field summed for the inline grid total |
628
-
| **relatedList** | `boolean \| 'primary'` | optional | Show this child collection as a related list on the parent's detail page (read-side mirror of inlineEdit). false = suppress; true/absent = shown as a supporting list, a side card in the record's related column (the shared "Related" tab on the tabbed page); 'primary' = part of the record's main content, drawn in the record's body (the details column, after its first field section) as a full-width table (inside the Details tab on the tabbed page, with no tab of its own). Absent on a master_detail field that sets inlineEdit, the list is 'primary' by default; write true to keep it a side card. 'primary' places a list and does not order it: the parent object's relatedLists orders the body tables and the side cards (ADR-0085). A derived related list (relatedList: 'primary') inherits its row order from the child object's DEFAULT list view sort — the isDefault view, or the first declared list item when none is marked default; wire spelling is sort=`<field>` / sort=-`<field>`, never $orderby. A child object with no list-view sort emits no ordering parameter, and rows fall back to record-id order. |
628
+
| **relatedList** | `boolean \| 'primary'` | optional | Show this child collection as a related list on the parent's detail page (read-side mirror of inlineEdit). false = the child object is not shown on the parent record page; true/absent = shown as an auxiliary panel on the record page; 'primary' = part of the record's main content, shown as a full-width table in the record's detail body. Absent on a master_detail field that sets inlineEdit, the list is 'primary' by default; write true to keep it an auxiliary panel. 'primary' places a list and does not order it: the parent object's relatedLists orders the full-width tables and the auxiliary panels. The page layout is defined by ADR-0085. A derived related list (relatedList: 'primary') inherits its row order from the child object's DEFAULT list view sort — the isDefault view, or the first declared list item when none is marked default; wire spelling is sort=`<field>` / sort=-`<field>`, never $orderby. A child object with no list-view sort emits no ordering parameter, and rows fall back to record-id order. |
629
629
|**relatedListTitle**|`string`| optional | Title for the detail-page related list |
630
630
|**relatedListColumns**|`string[]`| optional | Explicit columns for the detail-page related list, as child field names (e.g. ['name', 'status']); derived from the child object (highlightFields → field walk) when omitted. Strings only — labels, cell types and formatting always derive from the child object's field definitions; column objects are refused at parse. |
631
631
|**relatedListFilter**|`any`| optional | Declarative default filter for the detail-page related list: AND-composed with the parent-relationship condition `{ [referenceField]: parentId }` — an authored constraint, never a user-editable suggestion. The related-list tab badge count honors the same composed filter, so counts match the visible rows. Canonical Query-DSL FilterCondition (the same dialect as a query `where`), e.g. `{ status: { $ne: 'deleted' } }` to hide soft-deleted children. |
Copy file name to clipboardExpand all lines: content/docs/references/system/migration.mdx
+2-2Lines changed: 2 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -88,7 +88,7 @@ Add a new field to an existing object
88
88
|**inlineTitle**|`string`| optional | Title for the inline master-detail grid |
89
89
|**inlineColumns**|`{ name: string; label?: string; type?: Enum<'text' \| 'number' \| 'currency' \| 'date' \| 'datetime' \| 'time' \| 'select' \| 'lookup' \| 'file'>; width?: number; … }[]`| optional | Explicit columns for the inline grid (derived from the child object when omitted). Each entry is a strict, name-keyed column (`{ name, label?, type?, … }` — objectui GridColumn); identity-only entries (`{ name }`) hydrate everything else from the child object's fields. Unknown keys and the retired `field` spelling are refused at parse. |
90
90
|**inlineAmountField**|`string`| optional | Numeric child field summed for the inline grid total |
91
-
| **relatedList** | `boolean \| 'primary'` | optional | Show this child collection as a related list on the parent's detail page (read-side mirror of inlineEdit). false = suppress; true/absent = shown as a supporting list, a side card in the record's related column (the shared "Related" tab on the tabbed page); 'primary' = part of the record's main content, drawn in the record's body (the details column, after its first field section) as a full-width table (inside the Details tab on the tabbed page, with no tab of its own). Absent on a master_detail field that sets inlineEdit, the list is 'primary' by default; write true to keep it a side card. 'primary' places a list and does not order it: the parent object's relatedLists orders the body tables and the side cards (ADR-0085). A derived related list (relatedList: 'primary') inherits its row order from the child object's DEFAULT list view sort — the isDefault view, or the first declared list item when none is marked default; wire spelling is sort=`<field>` / sort=-`<field>`, never $orderby. A child object with no list-view sort emits no ordering parameter, and rows fall back to record-id order. |
91
+
| **relatedList** | `boolean \| 'primary'` | optional | Show this child collection as a related list on the parent's detail page (read-side mirror of inlineEdit). false = the child object is not shown on the parent record page; true/absent = shown as an auxiliary panel on the record page; 'primary' = part of the record's main content, shown as a full-width table in the record's detail body. Absent on a master_detail field that sets inlineEdit, the list is 'primary' by default; write true to keep it an auxiliary panel. 'primary' places a list and does not order it: the parent object's relatedLists orders the full-width tables and the auxiliary panels. The page layout is defined by ADR-0085. A derived related list (relatedList: 'primary') inherits its row order from the child object's DEFAULT list view sort — the isDefault view, or the first declared list item when none is marked default; wire spelling is sort=`<field>` / sort=-`<field>`, never $orderby. A child object with no list-view sort emits no ordering parameter, and rows fall back to record-id order. |
92
92
|**relatedListTitle**|`string`| optional | Title for the detail-page related list |
93
93
|**relatedListColumns**|`string[]`| optional | Explicit columns for the detail-page related list, as child field names (e.g. ['name', 'status']); derived from the child object (highlightFields → field walk) when omitted. Strings only — labels, cell types and formatting always derive from the child object's field definitions; column objects are refused at parse. |
94
94
|**relatedListFilter**|`any`| optional | Declarative default filter for the detail-page related list: AND-composed with the parent-relationship condition `{ [referenceField]: parentId }` — an authored constraint, never a user-editable suggestion. The related-list tab badge count honors the same composed filter, so counts match the visible rows. Canonical Query-DSL FilterCondition (the same dialect as a query `where`), e.g. `{ status: { $ne: 'deleted' } }` to hide soft-deleted children. |
@@ -522,7 +522,7 @@ Add a new field to an existing object
522
522
|**inlineTitle**|`string`| optional | Title for the inline master-detail grid |
523
523
|**inlineColumns**|`{ name: string; label?: string; type?: Enum<'text' \| 'number' \| 'currency' \| 'date' \| 'datetime' \| 'time' \| 'select' \| 'lookup' \| 'file'>; width?: number; … }[]`| optional | Explicit columns for the inline grid (derived from the child object when omitted). Each entry is a strict, name-keyed column (`{ name, label?, type?, … }` — objectui GridColumn); identity-only entries (`{ name }`) hydrate everything else from the child object's fields. Unknown keys and the retired `field` spelling are refused at parse. |
524
524
|**inlineAmountField**|`string`| optional | Numeric child field summed for the inline grid total |
525
-
| **relatedList** | `boolean \| 'primary'` | optional | Show this child collection as a related list on the parent's detail page (read-side mirror of inlineEdit). false = suppress; true/absent = shown as a supporting list, a side card in the record's related column (the shared "Related" tab on the tabbed page); 'primary' = part of the record's main content, drawn in the record's body (the details column, after its first field section) as a full-width table (inside the Details tab on the tabbed page, with no tab of its own). Absent on a master_detail field that sets inlineEdit, the list is 'primary' by default; write true to keep it a side card. 'primary' places a list and does not order it: the parent object's relatedLists orders the body tables and the side cards (ADR-0085). A derived related list (relatedList: 'primary') inherits its row order from the child object's DEFAULT list view sort — the isDefault view, or the first declared list item when none is marked default; wire spelling is sort=`<field>` / sort=-`<field>`, never $orderby. A child object with no list-view sort emits no ordering parameter, and rows fall back to record-id order. |
525
+
| **relatedList** | `boolean \| 'primary'` | optional | Show this child collection as a related list on the parent's detail page (read-side mirror of inlineEdit). false = the child object is not shown on the parent record page; true/absent = shown as an auxiliary panel on the record page; 'primary' = part of the record's main content, shown as a full-width table in the record's detail body. Absent on a master_detail field that sets inlineEdit, the list is 'primary' by default; write true to keep it an auxiliary panel. 'primary' places a list and does not order it: the parent object's relatedLists orders the full-width tables and the auxiliary panels. The page layout is defined by ADR-0085. A derived related list (relatedList: 'primary') inherits its row order from the child object's DEFAULT list view sort — the isDefault view, or the first declared list item when none is marked default; wire spelling is sort=`<field>` / sort=-`<field>`, never $orderby. A child object with no list-view sort emits no ordering parameter, and rows fall back to record-id order. |
526
526
|**relatedListTitle**|`string`| optional | Title for the detail-page related list |
527
527
|**relatedListColumns**|`string[]`| optional | Explicit columns for the detail-page related list, as child field names (e.g. ['name', 'status']); derived from the child object (highlightFields → field walk) when omitted. Strings only — labels, cell types and formatting always derive from the child object's field definitions; column objects are refused at parse. |
528
528
|**relatedListFilter**|`any`| optional | Declarative default filter for the detail-page related list: AND-composed with the parent-relationship condition `{ [referenceField]: parentId }` — an authored constraint, never a user-editable suggestion. The related-list tab badge count honors the same composed filter, so counts match the visible rows. Canonical Query-DSL FilterCondition (the same dialect as a query `where`), e.g. `{ status: { $ne: 'deleted' } }` to hide soft-deleted children. |
0 commit comments