From 46c30d4c013892b58bdb3ee12412a32d3fba1841 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 30 Oct 2026 04:33:45 +0000 Subject: [PATCH 1/2] docs(skills): a primary related list is a body table in the Details tab, and the relatedLists refusal states its package boundary objectstack-ui SKILL.md and objectstack-data rules/relationships.md still taught a relatedList: 'primary' child's "own tab" and an unbounded relatedLists refusal. Since the record page v3 and the rewritten spec describes, a primary list is a full-width body table in the details column after the first field section (inside the Details tab); every other list is a side card in the shared Related tab; field changes join the Timeline tab under its Changes filter. The refusal now says which unknown names are refused where. The removed app-level relatedLayout is no longer taught as a surviving override. Restated sentences in relationships.md pay for the added words; neither ceiling moves. Co-authored-by: Claude Claude-Session: https://claude.ai/code/session_016WV4cyuFKhHVaGYSSdTHG8 --- .../27354-skills-related-list-body-table.md | 11 +++++++ .../objectstack-data/rules/relationships.md | 29 ++++++++++--------- skills/objectstack-ui/SKILL.md | 25 ++++++++-------- 3 files changed, 38 insertions(+), 27 deletions(-) create mode 100644 .changeset/27354-skills-related-list-body-table.md diff --git a/.changeset/27354-skills-related-list-body-table.md b/.changeset/27354-skills-related-list-body-table.md new file mode 100644 index 00000000000..1a6e35fd6ef --- /dev/null +++ b/.changeset/27354-skills-related-list-body-table.md @@ -0,0 +1,11 @@ +--- +'@objectstack/skills': patch +--- + +docs(skills): objectstack-ui and objectstack-data teach a primary related list as a body table in the Details tab, and the relatedLists refusal with its package boundary + +Clause-②: no +Metadata-protocol: none + +- **Before.** Both skills said a `relatedList: 'primary'` child gets its own tab on the record page, `objectstack-ui` described one tab per primary list plus a shared Related tab and an app-level `relatedLayout` override, and `objectstack-data` said a `relatedLists` name matching no inbound FK is refused, with no boundary. +- **Now.** A primary list is a full-width body table in the details column after the first field section (inside the Details tab, no tab of its own); every other list is a side card in the shared Related tab; field changes join the Timeline tab under its Changes filter. A `relatedLists` child in the stack with no inbound FK is refused everywhere; one that exists nowhere is refused by `os validate` / `os build` and by a metadata write naming no package, while a write into a package stores it and the page places nothing. No ceiling moves; no schema, key or behaviour changes. diff --git a/skills/objectstack-data/rules/relationships.md b/skills/objectstack-data/rules/relationships.md index 302f1603668..c436d3e0764 100644 --- a/skills/objectstack-data/rules/relationships.md +++ b/skills/objectstack-data/rules/relationships.md @@ -229,8 +229,7 @@ Declare inline editing **on the relationship**, in the data model — not in a f view. Set `inlineEdit: true` on the child's `master_detail` (or `lookup`) field that points back to the parent. The parent's **standard** create/edit form then renders an editable grid for these children and saves parent + children in **one -atomic transaction** — with no form-view config and no bespoke page. The UI is -derived from metadata (relationship FK + child fields → grid columns). +atomic transaction** — with no form-view config and no bespoke page. ```typescript // On the CHILD object's FK field: @@ -271,10 +270,9 @@ inlineEdit: 'grid' // editable line-item grid (fast bulk entry; thin childre inlineEdit: 'form' // read-only list; "Add" / per-row edit opens the FULL form ``` -- **`'grid'`** — spreadsheet-like editable grid. Best for *thin* line items +- **`'grid'`** — best for *thin* line items (invoice/order lines): few columns, high volume, keyboard-fast. -- **`'form'`** — compact read-only list; Add / per-row edit opens the child's - complete form. Best for *fat* children (long text, attachments, many fields) +- **`'form'`** — best for *fat* children (long text, attachments, many fields) that don't fit a narrow grid cell. - **`true`** / omitted — **smart default**: picks `form` when the child has rich/form-only fields (textarea, file, image, json, location…) or more than ~8 @@ -320,11 +318,10 @@ InvoiceLine = { ### Detail-page related lists (the read-side mirror) -Where `inlineEdit` is the **write** side (child pulled into the parent's entry -form), the **related list** on the parent's record detail page is the **read** +Where `inlineEdit` is the **write** side, the **related list** on the parent's record detail page is the **read** side. You usually don't declare it: every child relationship -(`master_detail` and `lookup`) is shown as a related list on the parent's detail -page **by default** — owned (`master_detail`) children first. The relationship +(`master_detail` and `lookup`) is shown **by default** — owned (`master_detail`) +children first. The relationship flags exist to *refine* that: ```typescript @@ -333,7 +330,7 @@ project: { type: 'master_detail', reference: 'project', inlineEdit: true, // write side: edit inline in the Project form - relatedList: 'primary', // read side: a body table (default with inlineEdit) + relatedList: 'primary', // read side: a body table relatedListTitle: 'Tasks', // title of the detail-page list relatedListColumns: ['title', 'status', 'priority', 'due_date'], }, @@ -344,16 +341,20 @@ audit_ref: { type: 'master_detail', reference: 'invoice', relatedList: false }, `relatedList` is tri-state — placement (ADR-0085): -- `'primary'` — main content: a full-width table in the record's body (its own - tab on the tabbed page). Default for a `master_detail` FK with `inlineEdit`. +- `'primary'` — main content: a full-width table in the details column after + the first field section (in the Details tab, no tab of its own). Default for a + `master_detail` FK with `inlineEdit`. - `false` — hidden from the parent's page. - `true`, or omitted (except inline-edited `master_detail`) — a side card (the shared **Related** tab). - Order is the parent's: `relatedLists: ['task', 'event.related_to']` (child, or - `object.field`) lists those first; a name matching no inbound FK is refused. + `object.field`) lists those first. A child in the stack with no inbound FK is + refused everywhere; one that exists nowhere is refused by `os validate` / + `os build` and by a metadata write naming no package — a write into a package + stores it, and the page places nothing. `relatedListTitle` / `relatedListColumns` override the derived title / columns; -both are optional — when omitted, columns auto-derive from the child object's +otherwise columns auto-derive from the child object's `highlightFields` (the same source the lookup picker uses). Audit FKs (`created_by`/`updated_by`/`owner_id`) never become related lists. A child that references the parent through several FKs (e.g. `primary_account` + diff --git a/skills/objectstack-ui/SKILL.md b/skills/objectstack-ui/SKILL.md index 152c439e21c..0aebe356f19 100644 --- a/skills/objectstack-ui/SKILL.md +++ b/skills/objectstack-ui/SKILL.md @@ -106,25 +106,24 @@ and the form's `taxRateField` overrides which parent field drives the totals. related list on the parent's record DETAIL page. You don't author it: every child relationship is shown as a related list by default (owned `master_detail` children first). Refine on the relationship: -- `relatedList: 'primary'` — main content: a body table, its **own tab** on the - tabbed page (see layout below); it places, the parent's `relatedLists` orders - (ADR-0085). +- `relatedList: 'primary'` — main content: a full-width body table (see layout + below); it places, the parent's `relatedLists` orders (ADR-0085). - `relatedList: false` — suppress a noisy child from the detail page. - `relatedListTitle` / `relatedListColumns` — override the derived title / columns (both optional; columns otherwise auto-derive from the child object's `highlightFields`). See objectstack-data → Relationships → Detail-page related lists. **Related-list layout.** On the synthesized record page, each -`relatedList: 'primary'` child (default: inline-edited `master_detail`) gets its -**own tab**; every other list stacks under one shared **Related** tab, both in -the object's `relatedLists` order. Promoting a child table to a tab is a -one-word change on the relationship. The object still declares no per-surface -*layout* hints: the old -`detail.relatedLayout` toggle and object-level `detail: {...}` block stay -**removed** (ADR-0085); `relatedLayout: 'tabs' | 'stack'` survives only as an -app-level default override, not an object key. For arrangements the -relationship layer can't express — filtered splits (e.g. Open vs Closed tabs), a -chart/report tab — assign the object a **custom record Page** and lay it out +`relatedList: 'primary'` child (default: inline-edited `master_detail`) is a +body table in the details column after the first field section (in the +Details tab, no tab of its own); every other list is a side card in one shared +**Related** tab, both in the object's `relatedLists` order. Field changes +(`trackHistory`) join the Timeline tab under its Changes filter; there is no +History tab. The object declares no per-surface *layout* hints: the +`detail.relatedLayout` toggle, object-level `detail: {...}` block and app-level +`relatedLayout` are **removed** (ADR-0085). For arrangements the relationship +layer can't express — filtered splits (e.g. Open vs Closed tabs), a chart/report +tab — assign the object a **custom record Page** and lay it out explicitly with `record:related_list` (or inline-editable `line_items`) blocks. ### Field Conditional Rules in Forms From 8c62baf0c2a6ea6f0d079f56eb0ee0d4621200c7 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 30 Oct 2026 05:09:36 +0000 Subject: [PATCH 2/2] docs(skills): the relatedLists refusal names what os validate / os build leave to the runtime The ordering bullet in objectstack-data rules/relationships.md now carries the describe's exception: os validate / os build leave a sys_ / cloud_ / ai_ or sibling-artifact child name to the runtime door (collectRelatedListNameErrors returns early for both). +21 tokens inside the existing headroom; no ceiling moves. Co-authored-by: Claude Claude-Session: https://claude.ai/code/session_016WV4cyuFKhHVaGYSSdTHG8 --- skills/objectstack-data/rules/relationships.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/skills/objectstack-data/rules/relationships.md b/skills/objectstack-data/rules/relationships.md index c436d3e0764..aa0277878fa 100644 --- a/skills/objectstack-data/rules/relationships.md +++ b/skills/objectstack-data/rules/relationships.md @@ -350,8 +350,9 @@ audit_ref: { type: 'master_detail', reference: 'invoice', relatedList: false }, - Order is the parent's: `relatedLists: ['task', 'event.related_to']` (child, or `object.field`) lists those first. A child in the stack with no inbound FK is refused everywhere; one that exists nowhere is refused by `os validate` / - `os build` and by a metadata write naming no package — a write into a package - stores it, and the page places nothing. + `os build` (which leave a `sys_` / `cloud_` / `ai_` or sibling-artifact name + to the runtime) and by a metadata write naming no package — a write into a + package stores it, and the page places nothing. `relatedListTitle` / `relatedListColumns` override the derived title / columns; otherwise columns auto-derive from the child object's