Repository navigation
Commit 1c66988
fix(studio): show, keep and author a controlled-by-parent object's optional master (#31202)
<!-- claude-projects-attribution: {"github_login":"os-project-manager"}
-->
_Requested by **Leehom** · [project
thread](https://claude.ai/code/project/chan_01TenzsRopZj4jMbvjX8uz1X?thread=cmsg_01TenzsRopZj4jMbvjX8uz1X1qmTvsS4RmPr4CF2WqFHpZ)_
Fixes #30974
Clause-②: yes (widening)
Metadata-protocol: none
## 维护者速读
- 改了什么:Studio 现在认得「可选主字段」:OWD 总览把 `required: false` 的唯一主从字段显示为主对象;Studio
保存不再把作者写的 `required: false` 改成 `true`;字段检查器里唯一主从字段的「必填」可以取消(写成
`required: false`),下方说明无主记录只有所有者和「查看所有记录」可见。
- 为什么改:#30905 裁决 A 允许受父级控制对象的唯一主从字段可选,Studio
仍按「必须必填」处理,保存会悄悄改写作者选定的访问模型(p2)。
- 风险与回滚:只动 `ui/packages/app-shell` 的 Studio 代码和一条 i18n 文案,不改
spec、不改运行时;回滚即还原本 PR。省略 `required` 的主字段仍按必填处理,两个以上主从字段时锁定照旧。
- 先看:`owd-sharing.ts`(主字段改由 spec 的 `controlledByParentMasterOf`
判定,保存只补省略的
`required`)、`ObjectFieldInspector.tsx`(`cbpOptionalMasterAllowed`
一段)、`owd-sharing.test.ts`(被 #30940 改标的那一行翻回一致)。
- 两分钟验证:`pnpm --dir ui exec vitest run
packages/app-shell/src/views/studio-design/owd-sharing.test.ts`,预期全绿,其中
"one master without required" 与 "one optional master (required: false)"
两行都显示主对象 `crm_account`。
- 需要你做的:无,席位落地。
## What changes
ADR-0055 §1 as amended by #30905 ruling A lets a `controlled_by_parent`
object's ONLY `master_detail` be declared `required: false`, an optional
master whose records may name none. Studio still treated every master as
required. This PR covers the card's three items:
- **Display** (`owd-sharing.ts`, `deriveMasterObject`). Which field is
the master is now the spec's own answer, `controlledByParentMasterOf`
from `@objectstack/spec/data`, so an optional master is shown as the
master and the two-master shapes stay as the spec rules them (Studio
does not re-decide them). An omitted `required` on the object's only
`master_detail` is read as required first, as the spec's stored-row
conversion `cbp-master-detail-required-defaulted` reads it, which is
ruling A's "Studio still defaults it to required".
- **Save** (`owd-sharing.ts`, `requireCbpMasterDetails`, used by
`PackageOwdOverviewPanel.tsx` and `ObjectSettingsPanel.tsx`). Switching
to Controlled by parent still sets `required: true` on each
`master_detail` that writes no `required` (the builder's force), and now
keeps an explicit `required: false`. The overview's 409 prerequisite
list (`cbpPrerequisite`) reads the same predicate,
`cbpMasterDetailDefaultsRequired`.
- **Authoring** (`ObjectFieldInspector.tsx`). On a cbp object's only
`master_detail` the Required box is no longer locked on: clearing it
writes `required: false` (never a dropped key, which would be defaulted
back to required), and the help text under it, new key
`designer.field.requiredCbpOptionalMaster` (en, zh), says a record that
names no master is visible only to its owner and to View All Records on
the object. Beside another `master_detail` the old lock and text stand,
because the spec admits no optional master there (builder and
`relationship/master-detail-required` refuse it).
## Why
Triage graded it p2: a Studio save silently turned an author's optional
master into a required one, which rewrites the access model they chose.
## Tests
- The two display-agreement rows PR #30940 re-labelled flip back to
agreement: "one master without required" in `owd-sharing.test.ts` and in
`PackageOwdOverviewPanel.cbpRequiredMaster-12326.test.tsx` now name
`crm_account`, and the spec parse accepts the shape. The per-row
"parses" override column is gone: every row's parse verdict now equals
"a master is named".
- New rows: one optional master (`required: false`), a `required: false`
master-detail beside the required one, two `master_detail` fields none
required.
- New: `requireCbpMasterDetails` keeps an explicit `required: false`
(map, array, and mixed with an omitted one); the overview save keeps it,
through the rendered dial and Save button.
- Inspector, driven through the rendered checkbox: the only
master-detail can be cleared to `required: false` and ticked back;
beside another master-detail the box stays locked with the old reason.
Run on head 3c18a37 (the code head; f7f9807 after it changes only the
changeset): the three files, 3/3 files, 38/38 tests passed.
<details><summary>Ablations (each mutation through
scripts/ablation-replace.mjs; every restore proven blob == HEAD and `git
diff HEAD` empty)</summary>
| Mutation | Red |
|:--|:--|
| `deriveMasterObject` without the omitted-required default | 2 (both
"one master without required" rows) |
| master judged by the old `required === true` count instead of
`controlledByParentMasterOf` | 3 (both optional-master rows, the
overview save test) |
| save predicate back to `required !== true` | 2 (both keeps-optional
tests) |
| inspector commit back to `v \|\| undefined` | 2 |
| inspector lock back to `cbpMasterDetail && required === true` | 2 |
| inspector help text back to the old key | 1 |
</details>
## Acceptance notes
- The overview names the master but does not say whether it is optional;
the inspector's help text is where the optional master is explained. No
new overview string was added.
- A stored object whose only master omits `required` shows an unticked
Required box (the box shows what is written), while the overview names
it as the master. Ticking writes `required: true`; switching the model
to Controlled by parent writes it too.
- `ObjectSettingsPanel.tsx` gets a comment-only edit: its comment said
the save forces every `master_detail` required, which this change makes
false.
- No `content/docs/frontend/` page describes the inspector's Required
lock; the cbp docs belong to #30907.
## Verification
All local readings on head f7f9807 unless noted; CI runs the rest of
the farm.
- Derived gate list (`node scripts/pm/dispatch-gates.mjs --commands
--repo objectstack-ai/objectstack`): 36 families, every one exit 0.
`--ran` reconciliation: "36 derived famil(ies) accounted for — 36 run, 0
NOT-MEASURED (a DERIVED zero — all 36 recorded an exit code and none of
them is 3)". The loop ran on 3c18a37; the four changeset families were
re-run on f7f9807 (exit 0), and `pnpm check:type-check-debt`, which
first answered exit 3 (it queued behind my own lock hold, nothing
measured), was re-run under the lock on f7f9807: exit 0.
- `pnpm --filter @object-ui/app-shell run type-check`: exit 0. `pnpm
--filter @object-ui/i18n run type-check`: exit 0.
- `pnpm --dir ui exec vitest run
packages/app-shell/src/views/studio-design/
packages/app-shell/src/views/metadata-admin/inspectors/ packages/i18n/`:
659/659 files, 7206 passed, 14 skipped. The whole app-shell suite (2,210
files) is left to CI's sharded Test UI.
- `pnpm --dir ui exec eslint` on the nine changed ui files: exit 0, 0
errors; the 17 warnings are all on lines this PR does not touch.
- `pnpm --dir ui run check:new-line-citations`: "0 new citation(s)",
exit 0.
- Builds first: the `@objectstack/spec`, `formula`, `client`, `lint`,
`sdui-parser` closures, then `@object-ui/app-shell^...`, both lock
VERDICT command-exit 0.
## Human Minimum
- [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic.
- [x] 2. Every name I added uses the casing for its kind.
- [x] 3. No formatter ran; `pnpm lint` passes. (The changed ui files
were linted locally, exit 0; the repo-wide run is CI's.)
- [x] 4. One issue, one branch off an up-to-date `main`, one pull
request.
- [x] 5. A change that publishes carries a changeset, and both
declaration lines are filled in here and in the changeset.
- [x] 6. `packages/spec` touched: its generated artifacts are
regenerated and committed. (Not touched.)
- [x] 7. No hand edits to `content/docs/references/`,
`packages/*/CHANGELOG.md` or `content/docs/releases/`.
- [x] 8. Title, body and comments are in English.
- [x] The *Before Submitting* list in CONTRIBUTING.md is done.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01FEkQ5mJLhZasj7G83LSvsv)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 3f4cb21 commit 1c66988
10 files changed
Lines changed: 239 additions & 102 deletions
File tree
- .changeset
- ui/packages
- app-shell/src/views
- metadata-admin/inspectors
- studio-design
- i18n/src/namespaces/studio
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
8 | 8 | | |
9 | 9 | | |
10 | 10 | | |
11 | | - | |
12 | | - | |
13 | | - | |
14 | | - | |
15 | | - | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
16 | 17 | | |
17 | 18 | | |
18 | 19 | | |
| |||
89 | 90 | | |
90 | 91 | | |
91 | 92 | | |
| 93 | + | |
92 | 94 | | |
93 | 95 | | |
94 | | - | |
95 | | - | |
| 96 | + | |
| 97 | + | |
96 | 98 | | |
97 | 99 | | |
98 | 100 | | |
99 | | - | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
100 | 105 | | |
101 | 106 | | |
102 | 107 | | |
| 108 | + | |
103 | 109 | | |
104 | 110 | | |
105 | 111 | | |
106 | 112 | | |
107 | 113 | | |
108 | | - | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
109 | 138 | | |
110 | 139 | | |
111 | 140 | | |
| |||
123 | 152 | | |
124 | 153 | | |
125 | 154 | | |
126 | | - | |
| 155 | + | |
127 | 156 | | |
128 | 157 | | |
129 | 158 | | |
| 159 | + | |
| 160 | + | |
130 | 161 | | |
131 | 162 | | |
132 | 163 | | |
| |||
175 | 206 | | |
176 | 207 | | |
177 | 208 | | |
178 | | - | |
| 209 | + | |
179 | 210 | | |
180 | 211 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1087 | 1087 | | |
1088 | 1088 | | |
1089 | 1089 | | |
1090 | | - | |
1091 | | - | |
1092 | | - | |
1093 | | - | |
| 1090 | + | |
| 1091 | + | |
| 1092 | + | |
| 1093 | + | |
| 1094 | + | |
| 1095 | + | |
| 1096 | + | |
| 1097 | + | |
| 1098 | + | |
| 1099 | + | |
| 1100 | + | |
| 1101 | + | |
| 1102 | + | |
1094 | 1103 | | |
1095 | 1104 | | |
| 1105 | + | |
| 1106 | + | |
1096 | 1107 | | |
1097 | 1108 | | |
1098 | 1109 | | |
| |||
1470 | 1481 | | |
1471 | 1482 | | |
1472 | 1483 | | |
1473 | | - | |
| 1484 | + | |
| 1485 | + | |
| 1486 | + | |
1474 | 1487 | | |
1475 | | - | |
1476 | | - | |
| 1488 | + | |
| 1489 | + | |
| 1490 | + | |
1477 | 1491 | | |
1478 | 1492 | | |
1479 | 1493 | | |
| |||
1485 | 1499 | | |
1486 | 1500 | | |
1487 | 1501 | | |
1488 | | - | |
| 1502 | + | |
1489 | 1503 | | |
1490 | 1504 | | |
1491 | 1505 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
207 | 207 | | |
208 | 208 | | |
209 | 209 | | |
210 | | - | |
211 | | - | |
212 | | - | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
213 | 215 | | |
214 | 216 | | |
215 | 217 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
6 | 6 | | |
7 | 7 | | |
8 | 8 | | |
9 | | - | |
10 | | - | |
11 | | - | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
12 | 15 | | |
13 | | - | |
14 | | - | |
15 | | - | |
16 | | - | |
17 | | - | |
18 | | - | |
19 | | - | |
20 | | - | |
21 | 16 | | |
22 | 17 | | |
23 | 18 | | |
| |||
62 | 57 | | |
63 | 58 | | |
64 | 59 | | |
65 | | - | |
66 | | - | |
67 | | - | |
| 60 | + | |
| 61 | + | |
68 | 62 | | |
| 63 | + | |
69 | 64 | | |
70 | | - | |
71 | | - | |
72 | | - | |
73 | | - | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
74 | 68 | | |
75 | 69 | | |
76 | 70 | | |
| |||
82 | 76 | | |
83 | 77 | | |
84 | 78 | | |
85 | | - | |
86 | | - | |
87 | | - | |
88 | | - | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
89 | 82 | | |
90 | 83 | | |
91 | 84 | | |
| |||
127 | 120 | | |
128 | 121 | | |
129 | 122 | | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
130 | 147 | | |
131 | 148 | | |
132 | 149 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
54 | 54 | | |
55 | 55 | | |
56 | 56 | | |
57 | | - | |
| 57 | + | |
58 | 58 | | |
59 | 59 | | |
60 | 60 | | |
| |||
69 | 69 | | |
70 | 70 | | |
71 | 71 | | |
72 | | - | |
73 | | - | |
74 | | - | |
75 | | - | |
76 | | - | |
77 | | - | |
78 | | - | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
79 | 80 | | |
80 | 81 | | |
81 | 82 | | |
| |||
138 | 139 | | |
139 | 140 | | |
140 | 141 | | |
141 | | - | |
| 142 | + | |
142 | 143 | | |
143 | 144 | | |
144 | 145 | | |
| |||
399 | 400 | | |
400 | 401 | | |
401 | 402 | | |
402 | | - | |
403 | | - | |
| 403 | + | |
| 404 | + | |
| 405 | + | |
| 406 | + | |
404 | 407 | | |
405 | 408 | | |
406 | 409 | | |
| |||
0 commit comments