Repository navigation
Commit 2e0e451
feat(platform-objects): User Preferences and Identity Links leave Setup's menu; the User page gains a Preferences tab (#23936)
Fixes #23803
Clause-②: no
## What this changes
Item 3 of the card: "User Preferences" and "Identity Links" leave
Setup's menu, and each stays reachable from the user its rows belong to.
- `packages/platform-objects/src/apps/setup-nav.contributions.ts`: the
`nav_accounts` (`sys_account`, view `all_links`) and
`nav_user_preferences` (`sys_user_preference`, view `all_preferences`)
entries are removed. A comment in their place records why, and where the
rows are reached instead.
- `packages/platform-objects/src/pages/sys-user.page.ts`:
`SysUserDetailPage` gains a last tab, "Preferences" (en / zh-CN / ja-JP
/ es-ES, like its siblings). It is a `record:related_list` over
`sys_user_preference` on `user_id`, with the columns `key`,
`created_at`, `updated_at` (the `all_preferences` view's columns minus
`user_id`, which is the page's own record), sorted by `updated_at`
descending like that view, 25 rows per page with the list's own paging.
It authors no `add` and no `actions`. The existing "Linked Accounts" tab
over `sys_account` is unchanged and serves "Identity links" (measured
below), so no second tab is added.
- Translations: the two ids' labels leave `en.ts`, `zh-CN.ts`,
`ja-JP.ts` and `es-ES.ts`, and their recorded digests leave the three
`*.source-hashes.ts` tables (an orphan digest reds
`source-hash.test.ts`). Both ids join `DEAD_SETUP_NAV_IDS` in
`setup-nav-dead-key-tombstone.test.ts`, with their reason in its header.
- Tests in `packages/platform-objects`: the new
`setup-nav-per-user-stores.test.ts` pins (1) no Setup entry this package
contributes targets either object, by `objectName` or by a URL; (2) the
User page has one related list over each object, keyed on `user_id`,
accepted by `RecordRelatedListProps`, and the page parses through
`PageSchema`; (3) the Preferences list shows a subset of the
`all_preferences` columns and authors no `add` or `actions`; (4)
`unlink_account` is a `list_item` action. In
`caller-scoped-first-list-view.test.ts`, the non-vacuity control drops
`sys_user_preference`, because no nav entry names it now and the
population is derived from nav; a new case holds pin (a) for that object
by name, and goes red if an entry names it again.
- `packages/cli/test/connect-agent-both-halves-wire.pin.test.ts` (claim
amendment 2): the two Setup-tree length pins move from 34 to 32, the
count comment says 23 contributed entries, and the first case asserts
`nav_accounts` and `nav_user_preferences` are absent. 32 is measured
from the built packages: 8 groups (`group_approvals` is empty and
collapses), plus 23 `platform-objects` entries, plus the MCP entry.
- `packages/qa/dogfood/test/platform-app-object-entry-views.test.ts`
(claim amendment 3): the composed-boot copy of the same invariant gets
the same change. `sys_user_preference` leaves the non-vacuity list, and
a new case pins that no merged Setup or Account entry names it and that
its first declared list view is still not caller-scoped (read from the
composition, or else the identity barrel).
- `content/docs/ui/setup-app.mdx` (claim amendment 3): the Advanced row
of the group table lists "OAuth Applications" only. No other sentence on
the page names the two entries.
- `docs/qa/platform-checklist/areas/platform-core.json` (claim amendment
3): in `platform-core.builtin-apps-nav-render`, the Setup detail step no
longer lists Identity Links and User Preferences among Setup's rendered
destinations. It now has the runner open a user from Users and check the
Preferences and Linked Accounts tabs. Revision 5 becomes 6, and no
acceptance clause changes. The "45 Setup destinations" count is left as
it is: it belongs to item 4, which whichever change lands last
re-measures.
- `docs/qa/platform-checklist/areas/identity-auth.json`: item
`identity-auth.linked-accounts-social` cited
`setup-nav.contributions.ts#nav_accounts`. It now cites
`sys-user.page.ts#SysUserDetailPage`. Revision 3 becomes 4, as
provenance only, with no clause change. Without the re-point, `pnpm
check:platform-checklist` exits 1 (`ABSENT SYMBOL`, measured at
`2abfd4b4c`).
- `packages/platform-objects/src/identity/sys-account.object.ts` and
`sys-user-preference.object.ts`: comments only. They no longer say a
Setup entry names the object's unscoped view.
- Changeset: `@objectstack/platform-objects` patch, worded for
administrators.
No pin was deleted, and no assertion was loosened.
## The card's other items
- Item 1 (one audit entry) is carried by #23890.
- Item 2 (Packages and Installed Apps) was ruled B on decision card
#23892 (6216295104). It executes as objectstack-ai/objectui#12675 and
#23933.
- Item 4 (39 entries) is checked by whichever of this PR, #23890 and the
#23892 work (objectstack-ai/objectui#12675, #23933) lands last. This PR
pins only its own part: no top-level Setup entry targets
`sys_user_preference` or `sys_account`.
## Measured before the change (`origin/main` at `497518c3f`)
1. **Producer.** `nav_accounts` and `nav_user_preferences` were at
`setup-nav.contributions.ts` lines 186 and 187, in
`SETUP_NAV_CONTRIBUTIONS`, group `group_advanced`. Every other `app:
'setup'` contributor (cloud-connection twice, mcp, plugin-approvals,
plugin-audit, plugin-auth, plugin-security, plugin-sharing,
plugin-webhooks, service-datasource, service-messaging) was grepped for
`objectName:`, and none names either object.
2. **The User page.** `SysUserDetailPage` had "Linked Accounts" over
`sys_account` (`relationshipField: 'user_id'`) and no tab over
`sys_user_preference`. The new tab follows the Sessions and Linked
Accounts shape.
3. **Every preference row belongs to a user.** `user_id` is
`Field.lookup('sys_user', { required: true })`. The record validator
refuses an insert without it, and refuses a PATCH that clears it
(`required` and `required_cleared` in `record-validator.ts`). A user
delete turns the default `set_null` on a required foreign key into
`restrict` (`engine.ts`), so deleting a user cannot detach rows. The one
producer, objectui's `packages/data-objectstack/src/userState.ts`,
writes `user_id: userId` on every insert. Nothing in this repo writes
the object server-side, and tenant-wide settings live on `sys_setting`
(ADR-0007). No live database was read, so a row written below the engine
(a raw driver write) is outside this reading.
4. **Row actions on the tab and on the old grid.** Read in objectui at
the pin `daeb77fc84` (the files read are byte-identical at objectui
`main` `63d00da`). The object grid gates New, row Edit and row Delete
through `resolveAffordance` with `listNew`, `rowEdit` and `rowDelete`.
The related list gates them through `relatedNew`, `relatedRowEdit` and
`relatedRowDelete` (`packages/core/src/utils/affordanceGrants.ts`). Each
pair reads the same CRUD bit (managed-object policy and effective API
operations) and the same grant, so New, Edit and Delete get the same
verdict on the tab as on the grid. The tab does not have the grid's
Import, inline cell edit, bulk delete or `by_user` grouping view. Those
stay on the object's own list. Its route is unchanged (the console
resolves `/apps/setup/sys_user_preference` from all objects, not from
the menu), and a preference record's breadcrumb opens it.
`sys_account`'s one row action, `unlink_account`, has `locations:
['list_item', 'record_header']`, and the related-list host offers a
child object's `list_item` actions on its rows.
5. **Pins naming the ids or objects.**
`caller-scoped-first-list-view.test.ts` (control updated, pin (a) kept
by name), `setup-nav-dead-key-tombstone.test.ts` (ledger extended) and
`source-hash.test.ts` (orphan check, satisfied by removing the digests).
`console-page-nav-entries.test.ts` names neither.
## Search
"From the search" now holds through the user, not directly. Measured in
objectui at the same pin: the command palette (`CommandPalette.tsx`) and
the full search page (`SearchResultsPage.tsx`) take their object list
and their record-search scope from the active app's menu entries. So
after this change, typing "preferences" in Setup's palette no longer
offers the "User Preferences" list. `sys_user` stays in scope
(`nav_users`, and `enable.searchable: true`), so searching for a user's
name opens their page, where both tabs are. Neither object's rows were
ever searchable: both declare `enable.searchable: false`. A direct
palette hit for an object no menu names would be an objectui change, and
none is made here.
## Acceptance notes
- `docs/qa/platform-checklist/areas/platform-core.json` still records
"45 Setup destinations" in the Account-detail step. That count is item
4's, re-measured by whichever of this card's changes lands last.
- `packages/plugins/plugin-audit/src/audit-writers.ts` (the comment
above the per-user UI-state map) and `audit-writers.test.ts` (a case
comment) describe an admin editing a row "from Setup's User Preferences
grid". The behaviour they describe is unchanged, because an admin's edit
from the Preferences tab is the same write. Only the place name is out
of date. Not edited, because `plugin-audit` is outside this claim. No
carrier.
- `showViewAll` on `record:related_list` (spec `RecordRelatedListProps`,
default `true`, described as 'Show "View All" link to see all related
records') is not read by any objectui renderer. `RelatedList.tsx`
renders its View all link only when a host passes `onViewAll`, and no
host does (objectui pin `daeb77fc84`). The new tab sets `showViewAll:
true` like its eight siblings on this page. That is the default, so it
changes nothing. Filed by the seat as #23941.
## Verification, at head `770f1d617` (after merging `origin/main` at
`6d1660dc3`)
- `pnpm --filter @objectstack/platform-objects test`: 73 files and 1136
tests passed.
- `pnpm --filter @objectstack/cli exec vitest run
test/connect-agent-both-halves-wire.pin.test.ts`: 1 file and 2 tests
passed. At `a6f35a120`, before the edit, CI's Test Core (2/6) showed it
red: "expected [...] to have a length of 34 but got 32".
- `pnpm --filter @objectstack/dogfood exec vitest run
test/platform-app-object-entry-views.test.ts`: before the edit, 1 failed
and 49 passed (exit 1), with "(a) no longer judges sys_user_preference".
After the edit, 51 passed (exit 0). The 21 dogfood `*.test.ts` files
that are not `*.dogfood.test.ts` also ran together: 329 passed and 6
skipped. That subset is chosen by file name, not by whether a file
boots; some of the 21 boot a kernel in-process. The 244
`*.dogfood.test.ts` files boot example apps and run in CI's Dogfood
Regression Gate. None of them reads Setup's menu, and the two that name
`sys_user_preference` read it over the data API, which this change does
not touch.
- `pnpm --filter @objectstack/platform-objects typecheck`: exit 0 at
`a6f35a120`. The three edited or new test files are in the
`tsconfig.test.json` program (counted with `--listFiles`).
- `pnpm check:i18n`, `pnpm check:platform-checklist` (822/832 anchors
resolved, 10 on the named residual) and `pnpm check:doc-authoring`: each
exits 0.
- `node scripts/pm/dispatch-gates.mjs --commands` (no paths): 94
commands for the 20 changed paths. 93 exited 0: two needed their
prerequisite built first (`check:i18n` needed the CLI, and
`check:skill-examples` needed `client-react`), then exited 0.
`check:dual-build-cjs-loads` exited 3 (PREREQUISITE NOT MET: it reads
the whole workspace's `dist`), so it is NOT MEASURED locally; CI runs it
in Build Core, which is green on this head. `--ran` reconciliation: 94
derived, 93 run, 1 NOT MEASURED, 0 UNRUN.
- Ablation, one leg per platform-objects behaviour, each landed with
`scripts/ablation-replace.mjs` (it proves the anchor hit and restores
the blob to HEAD):
- re-adding `nav_user_preferences` turns 3 tests red;
- adding `value` to the Preferences columns turns 1 test red;
- pointing the tab's list at another object turns 3 tests red.
- After every leg, `git diff HEAD` is empty.
- CI's families are not re-run here: read them on this PR's checks.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01TQLy3Yq6vF4SDpTXdr3Uwi)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 2054a08 commit 2e0e451
20 files changed
Lines changed: 296 additions & 40 deletions
File tree
- .changeset
- content/docs/ui
- docs/qa/platform-checklist/areas
- packages
- cli/test
- platform-objects/src
- apps
- translations
- identity
- pages
- qa/dogfood/test
| 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 | |
|---|---|---|---|
| |||
55 | 55 | | |
56 | 56 | | |
57 | 57 | | |
58 | | - | |
| 58 | + | |
59 | 59 | | |
60 | 60 | | |
61 | 61 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1116 | 1116 | | |
1117 | 1117 | | |
1118 | 1118 | | |
1119 | | - | |
| 1119 | + | |
1120 | 1120 | | |
1121 | 1121 | | |
1122 | 1122 | | |
| |||
1182 | 1182 | | |
1183 | 1183 | | |
1184 | 1184 | | |
1185 | | - | |
| 1185 | + | |
1186 | 1186 | | |
1187 | 1187 | | |
1188 | 1188 | | |
1189 | 1189 | | |
1190 | 1190 | | |
1191 | 1191 | | |
1192 | | - | |
| 1192 | + | |
| 1193 | + | |
1193 | 1194 | | |
1194 | 1195 | | |
1195 | 1196 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
509 | 509 | | |
510 | 510 | | |
511 | 511 | | |
512 | | - | |
| 512 | + | |
513 | 513 | | |
514 | 514 | | |
515 | 515 | | |
| |||
532 | 532 | | |
533 | 533 | | |
534 | 534 | | |
535 | | - | |
| 535 | + | |
536 | 536 | | |
537 | 537 | | |
538 | 538 | | |
| |||
618 | 618 | | |
619 | 619 | | |
620 | 620 | | |
| 621 | + | |
| 622 | + | |
| 623 | + | |
| 624 | + | |
| 625 | + | |
| 626 | + | |
621 | 627 | | |
622 | 628 | | |
623 | 629 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
254 | 254 | | |
255 | 255 | | |
256 | 256 | | |
257 | | - | |
258 | | - | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
259 | 263 | | |
260 | 264 | | |
261 | 265 | | |
| |||
273 | 277 | | |
274 | 278 | | |
275 | 279 | | |
276 | | - | |
| 280 | + | |
277 | 281 | | |
278 | 282 | | |
279 | 283 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
145 | 145 | | |
146 | 146 | | |
147 | 147 | | |
148 | | - | |
| 148 | + | |
149 | 149 | | |
150 | 150 | | |
151 | 151 | | |
152 | 152 | | |
153 | 153 | | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
154 | 174 | | |
155 | 175 | | |
156 | 176 | | |
| |||
0 commit comments