Repository navigation
Commit 2b35374
fix(app-shell): a team made in Setup no longer reads as managed by an outside identity provider, and a header action refreshes the team's member count (#30417)
<!-- claude-projects-attribution: {"github_login":"os-bill"} -->
_Requested by **Bill** · [project
thread](https://claude.ai/code/project/chan_016iMXWSZDzqWRWvWzdNo8wg?thread=cmsg_016iMXWSZDzqWRWvWzdNo8wgHA15WwWBix9eGkVwzCLb4i)_
domain:ui#2 · os-bill · session_01Utp6Cb5R6JDdfyvtj3dPR2
Part of #30337
Clause-②: no
Metadata-protocol: none
## 维护者速读
- 改了什么:① 身份类对象(团队、用户、成员等 `better-auth`
表)的说明从「由身份提供方管理」改为「经内置身份服务写入」,正文说明用本页操作或登录/邀请/安全流程增改记录,10
个语言包同改,只改值不增删键;② 记录页上声明 `refreshAfter: true`
的操作成功后,除了重读本记录,还刷新本页列出的每个关联对象,关联列表重取、标签页与卡片计数重算。
- 为什么改:在 Setup
手工建的团队被标成「由身份提供方管理」,读起来像外部同步来的、不能改;用表头「添加成员」加人后,「团队成员」标签页仍显示 0(列表却有 2
条),要等列表里删一个人才追上。
- 风险与回滚:只动 `RecordDetailView.tsx` 的刷新范围(新增 `notifyViewChanged`,五处
`refreshAfter` 入口改用它)和文案;不动 spec、服务端、元数据。代价是带 `refreshAfter`
的操作后多发几次子列表读取,父记录仍只读一次(测试钉住)。回滚即 revert 本 PR。
- 先看:`ui/packages/app-shell/src/views/RecordDetailView.tsx` 的
`notifyViewChanged` 与 `pageChildObjectsRef` →
`RecordDetailView.childWriteRefreshesParent-12244.test.tsx` 末尾的 #30337
describe → `ui/packages/i18n/src/locales/zh.ts` 的
`managedByBadge.betterAuth`。
- 两分钟验证:`pnpm --dir ui exec vitest run
packages/app-shell/src/components/__tests__/ManagedByBadge.keyboard-27521.test.tsx
packages/app-shell/src/views/RecordDetailView.childWriteRefreshesParent-12244.test.tsx`,预期
10/10 通过(在 main 源码上新增的 3 条失败)。
- 需要你做的:无,席位落地。卡的第 2 项(成员行「标题」列显示原始 id)是 server 半边,见 Acceptance
notes,席位另立单。
## What changes
The card's three Expected bullets, and what this PR does for each:
> The label says something true of a team made here, or does not appear.
Done. `sys_team` declares `managedBy: 'better-auth'`, and that bucket is
right: the identity service owns the table and every write to it goes
through that service's endpoints (the team's own actions). The words
were wrong. The `managedByBadge.betterAuth` note now reads "Written
through the built-in identity service" / 「经内置身份服务写入」, and its body says
these records are created and changed with the actions on the page or
through sign-in, invitation and security flows, all of which go through
that service. That holds for every `better-auth` object, a hand-made
team included, and it names no outside provider. Values change in all
ten packs and in `ManagedByBadge`'s English fallback; no key is added or
removed. What the bucket means for other objects is unchanged: the badge
still draws for the same objects, under the same authoring gate, with
the same CRUD affordances hidden.
> Member rows read as names. If the title column adds nothing beside 用户,
it goes.
Not in this PR: only the server can fix it without the renderer
second-guessing declared metadata. See Acceptance notes.
> The tab count matches the list as soon as members change.
Done. The tab badge reads `RelatedCountStore`, keyed on the CHILD object
(`sys_team_member`) and invalidated by the data-change bus under that
name. The team's header action `add_team_member` (`type: 'api'`,
`refreshAfter: true`) succeeded and the page posted only `sys_team` and
this record. So the count cached when the team had no members stayed 0,
and the list read fresh when its tab opened. A row action on the
membership list posts `sys_team_member`, which is why the count caught
up after a removal.
- `refreshAfter` is the spec's "Refresh view after execution". On a
record page the view is the record and its related lists, so the page
now refreshes both: `notifyViewChanged` posts this record's change and
then one change per child object the page lists (`childRelations` plus
authored `record:related_list` / `record:reference_rail` children, the
same set the child-write subscription reads). The lists refetch, the
counts re-count, and the parent row is still read once, because the
bumps land in one batch.
- It is used at every `refreshAfter` entry on the page: the `api` action
door, the direct `api` fetch, the declared-operation script path, the
flow launch, and the server action handler's `onRefresh`. An action's
target is opaque to the client, so the page cannot know which objects it
wrote. The undo path and form saves keep the record-only refresh.
## Tests
- `ManagedByBadge.keyboard-27521.test.tsx`, a new top-level describe
(#30337): the `better-auth` tooltip in English (fallback) and in Chinese
(zh pack, through `I18nProvider`) names the built-in identity service
and the page's actions, and does not contain "identity provider" /
「身份提供方」.
- `RecordDetailView.childWriteRefreshesParent-12244.test.tsx`, a new
top-level describe (#30337) on the same harness: a header `type: 'api'`
action with `refreshAfter: true` whose door adds a child row
server-side. After the click the list shows the new row,
`RelatedCountStore` reads 3 (it was 2), and the parent is read exactly
once more. The harness's authenticated fetch is now a hoisted spy that
answers nothing unless a test gives it an implementation, as the bare
`vi.fn()` did.
- `RecordDetailView.setupManagedByChip-13385.test.tsx`: the two
header-note expectations follow the new title.
- Red first, on `4779e798` (tests only): `Tests 3 failed | 7 passed
(10)`. The two badge tests got the old note. The header-action test
found the door call but `Unable to find an element with the text:
Support plan`, because the list never refetched. Green after the fix:
`Test Files 3 passed (3)`, `Tests 15 passed (15)`. The red leg on the
parent commit is the ablation: the fix is the only source change, and
app-shell tests import `src`.
- Every ui test file that names `RecordDetailView`, `ManagedByBadge` or
`managedByBadge`, plus all of `packages/i18n/`: `Test Files 382 passed
(382)`, `Tests 3754 passed | 13 skipped (3767)` (under the verify lock,
17m12s).
- `pnpm exec turbo run type-check --filter=@object-ui/app-shell
--filter=@object-ui/i18n --concurrency=2` (under the verify lock):
`Tasks: 69 successful, 69 total`.
- `eslint` on the changed ui files: 0 errors. `RecordDetailView.tsx` has
124 warnings against 123 on main: one more `react-hooks/refs` heuristic
warning on the `withNewTabUrlFastPath(apiHandler, …)` memo, because
`apiHandler` now reaches a ref through `notifyViewChanged` inside a
callback. The other files carry no new warnings.
## Acceptance notes
- **Item 2 is the server half** (triage on the card says the same). The
标题 column is `sys_team_member.display_title`, the object's declared
`nameField`, a formula `record.user_id + ' in ' + record.team_id`. A
formula is evaluated on the stored row before `$expand`, so it can only
ever draw the two ids. A related list with no authored columns leads
with the declared name (`leadWithNameField`, ADR-0079), which exempts a
declared `nameField` by design, and then takes `highlightFields`
(`user_id`, then `team_id`, dropped as the key back to the team, then
`created_at`). The renderer cannot tell that this title adds nothing
without parsing the formula, which is second-guessing declared metadata
(`ui/AGENTS.md` #0.1). The producer-side fix sits in
`packages/platform-objects/src/identity/sys-team-member.object.ts`,
outside this card's file surface: `relatedListColumns: ['user_id',
'created_at']` on the `team_id` lookup (and `['team_id', 'created_at']`
on `user_id` for the user page's teams list). Authored relationship
columns are drawn as written, with no name lead, so the member list
would read 用户 / 创建时间. Reported to the seat to file. This PR says `Part
of`, so merging it leaves the card open for that half.
- `refreshAfter` refreshes more than it did on a record page. That is
the spec's wording ("the view"), and the change stays inside the page's
own lists. It is not a `'*'`: other mounted views are not refetched.
## Verification
<details><summary>Derived gates (dispatch-gates --commands, final tree
`53cf42ca`, after merging origin/main)</summary>
- `node scripts/check-adr-0087-registration.mjs --base origin/main` ::
exit 0
- `node scripts/check-adr-0087-registration.mjs --self-test` :: exit 0
- `node scripts/check-changeset-no-major.mjs --base origin/main` :: exit
0
- `node scripts/check-closing-keyword-parity.mjs` :: exit 0
- `node scripts/check-closing-keyword-parity.mjs --self-test` :: exit 0
- `node scripts/check-comment-mask-corpus.mjs` :: exit 0
- `node scripts/check-empty-changeset.mjs --base origin/main` :: exit 0
- `node scripts/check-empty-changeset.mjs --self-test` :: exit 0
- `node scripts/check-sdui-manifest.mjs` :: exit 0
- `node scripts/check-sdui-manifest.mjs --self-test` :: exit 0
- `node scripts/pm/release-rehearsal-clone.mjs --self-test` :: exit 0
- `node scripts/release-pending-publish.mjs --self-test` :: exit 0
- `pnpm --dir ui exec vitest run scripts/` :: exit 0
- `pnpm --filter @objectstack/spec run check:duration-unit-keys` :: exit
0
- `pnpm check:changeset-gate-self-tests` :: exit 0
- `pnpm check:closed-ceremony-text` :: exit 0
- `pnpm check:cross-package-test-inputs` :: exit 0
- `pnpm check:dispatcher-error-vocabulary` :: exit 0
- `pnpm check:driver-memory-census` :: exit 0
- `pnpm check:engine-double-contract` :: exit 0
- `pnpm check:gitlink-declared` :: exit 0
- `pnpm check:nul-bytes` :: exit 0
- `pnpm check:published-files` :: exit 0
- `pnpm check:query-options-erasure` :: exit 0
- `pnpm check:refd-timer-probe` :: exit 0
- `pnpm check:registry-log-declared` :: exit 0
- `pnpm check:rest-log-spy-declared` :: exit 0
- `pnpm check:skill-identifier-liveness` :: exit 0
- `pnpm check:test-source-alias` :: exit 0
- `pnpm check:tier-file-adoption` :: exit 0
- `pnpm check:type-check-coverage` :: exit 0
- `pnpm check:type-check-debt` :: exit 0
- `pnpm check:undeclared-dep-imports` :: exit 0
- `pnpm check:watch-hint-literal` :: exit 0
- `pnpm check:where-matcher` :: exit 0
`dispatch-gates --ran`: 35 derived, 35 run, 0 NOT-MEASURED, 0 UNRUN.
`pnpm check:type-check-debt` ran under the verify lock.
</details>
<details><summary>Final-head re-run</summary>
- On `53cf42ca`: the three changed test files plus
`RecordDetailView.updateActionRefresh-29935.test.tsx` (the
declared-operation refresh path): `Test Files 4 passed (4)`, `Tests 18
passed (18)`.
</details>
---
_Generated by [Claude
Code](https://claude.ai/code/session_01Utp6CZGJdHbf9pjnedaUeD)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent ac56acf commit 2b35374
16 files changed
Lines changed: 188 additions & 38 deletions
File tree
- .changeset
- ui/packages
- app-shell/src
- components
- __tests__
- views
- i18n/src/locales
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
155 | 155 | | |
156 | 156 | | |
157 | 157 | | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
158 | 162 | | |
159 | 163 | | |
160 | 164 | | |
161 | 165 | | |
162 | | - | |
| 166 | + | |
163 | 167 | | |
164 | | - | |
| 168 | + | |
165 | 169 | | |
166 | 170 | | |
167 | 171 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
8 | 8 | | |
9 | 9 | | |
10 | 10 | | |
| 11 | + | |
11 | 12 | | |
12 | 13 | | |
13 | 14 | | |
| |||
26 | 27 | | |
27 | 28 | | |
28 | 29 | | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
54 | 54 | | |
55 | 55 | | |
56 | 56 | | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
57 | 60 | | |
58 | 61 | | |
59 | 62 | | |
60 | | - | |
| 63 | + | |
61 | 64 | | |
62 | 65 | | |
63 | 66 | | |
| |||
192 | 195 | | |
193 | 196 | | |
194 | 197 | | |
195 | | - | |
| 198 | + | |
196 | 199 | | |
197 | 200 | | |
198 | 201 | | |
| |||
203 | 206 | | |
204 | 207 | | |
205 | 208 | | |
206 | | - | |
| 209 | + | |
207 | 210 | | |
208 | | - | |
| 211 | + | |
209 | 212 | | |
210 | 213 | | |
211 | 214 | | |
| |||
222 | 225 | | |
223 | 226 | | |
224 | 227 | | |
225 | | - | |
| 228 | + | |
226 | 229 | | |
227 | 230 | | |
228 | 231 | | |
| |||
243 | 246 | | |
244 | 247 | | |
245 | 248 | | |
246 | | - | |
| 249 | + | |
247 | 250 | | |
248 | | - | |
| 251 | + | |
249 | 252 | | |
250 | 253 | | |
251 | 254 | | |
| |||
354 | 357 | | |
355 | 358 | | |
356 | 359 | | |
| 360 | + | |
| 361 | + | |
| 362 | + | |
| 363 | + | |
| 364 | + | |
| 365 | + | |
| 366 | + | |
| 367 | + | |
| 368 | + | |
| 369 | + | |
| 370 | + | |
| 371 | + | |
| 372 | + | |
| 373 | + | |
| 374 | + | |
| 375 | + | |
| 376 | + | |
| 377 | + | |
| 378 | + | |
| 379 | + | |
| 380 | + | |
| 381 | + | |
| 382 | + | |
| 383 | + | |
| 384 | + | |
| 385 | + | |
| 386 | + | |
| 387 | + | |
| 388 | + | |
| 389 | + | |
| 390 | + | |
| 391 | + | |
| 392 | + | |
| 393 | + | |
| 394 | + | |
| 395 | + | |
| 396 | + | |
| 397 | + | |
| 398 | + | |
| 399 | + | |
| 400 | + | |
| 401 | + | |
| 402 | + | |
| 403 | + | |
| 404 | + | |
| 405 | + | |
| 406 | + | |
| 407 | + | |
| 408 | + | |
| 409 | + | |
| 410 | + | |
| 411 | + | |
| 412 | + | |
| 413 | + | |
| 414 | + | |
| 415 | + | |
| 416 | + | |
| 417 | + | |
| 418 | + | |
| 419 | + | |
| 420 | + | |
| 421 | + | |
| 422 | + | |
| 423 | + | |
| 424 | + | |
| 425 | + | |
| 426 | + | |
| 427 | + | |
| 428 | + | |
| 429 | + | |
| 430 | + | |
| 431 | + | |
| 432 | + | |
| 433 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
192 | 192 | | |
193 | 193 | | |
194 | 194 | | |
195 | | - | |
| 195 | + | |
196 | 196 | | |
197 | 197 | | |
198 | 198 | | |
199 | 199 | | |
200 | 200 | | |
201 | 201 | | |
202 | | - | |
| 202 | + | |
203 | 203 | | |
204 | 204 | | |
205 | 205 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
982 | 982 | | |
983 | 983 | | |
984 | 984 | | |
| 985 | + | |
| 986 | + | |
| 987 | + | |
| 988 | + | |
| 989 | + | |
| 990 | + | |
| 991 | + | |
| 992 | + | |
| 993 | + | |
| 994 | + | |
| 995 | + | |
| 996 | + | |
| 997 | + | |
| 998 | + | |
| 999 | + | |
| 1000 | + | |
985 | 1001 | | |
986 | 1002 | | |
987 | 1003 | | |
| |||
1686 | 1702 | | |
1687 | 1703 | | |
1688 | 1704 | | |
1689 | | - | |
| 1705 | + | |
1690 | 1706 | | |
1691 | 1707 | | |
1692 | 1708 | | |
| |||
1793 | 1809 | | |
1794 | 1810 | | |
1795 | 1811 | | |
1796 | | - | |
| 1812 | + | |
1797 | 1813 | | |
1798 | 1814 | | |
1799 | 1815 | | |
| |||
1889 | 1905 | | |
1890 | 1906 | | |
1891 | 1907 | | |
1892 | | - | |
| 1908 | + | |
1893 | 1909 | | |
1894 | 1910 | | |
1895 | 1911 | | |
| |||
1988 | 2004 | | |
1989 | 2005 | | |
1990 | 2006 | | |
1991 | | - | |
| 2007 | + | |
1992 | 2008 | | |
1993 | 2009 | | |
1994 | 2010 | | |
| |||
2009 | 2025 | | |
2010 | 2026 | | |
2011 | 2027 | | |
2012 | | - | |
2013 | | - | |
| 2028 | + | |
| 2029 | + | |
2014 | 2030 | | |
2015 | 2031 | | |
2016 | 2032 | | |
| |||
2019 | 2035 | | |
2020 | 2036 | | |
2021 | 2037 | | |
2022 | | - | |
| 2038 | + | |
2023 | 2039 | | |
2024 | 2040 | | |
2025 | 2041 | | |
| |||
2298 | 2314 | | |
2299 | 2315 | | |
2300 | 2316 | | |
| 2317 | + | |
| 2318 | + | |
2301 | 2319 | | |
2302 | 2320 | | |
2303 | 2321 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1017 | 1017 | | |
1018 | 1018 | | |
1019 | 1019 | | |
1020 | | - | |
1021 | | - | |
| 1020 | + | |
| 1021 | + | |
1022 | 1022 | | |
1023 | 1023 | | |
1024 | 1024 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
908 | 908 | | |
909 | 909 | | |
910 | 910 | | |
911 | | - | |
912 | | - | |
| 911 | + | |
| 912 | + | |
913 | 913 | | |
914 | 914 | | |
915 | 915 | | |
| |||
0 commit comments