Repository navigation
feat(platform-objects): User Preferences and Identity Links leave Setup's menu; the User page gains a Preferences tab - #23936
Conversation
…up's menu; the User page gains a Preferences tab Setup's nav_accounts (sys_account) and nav_user_preferences (sys_user_preference) entries are removed. Both stores hold rows that belong to one user; an administrator reaches them from the User record page: the existing Linked Accounts tab and a new Preferences tab, a related list on user_id whose columns are the all_preferences view's minus user_id. The two ids join the Setup nav tombstone, their labels and recorded source hashes leave the four bundles, and the caller-scoped-first-view control holds its (a) pin for sys_user_preference by name now that no nav entry names it. Claude-Session: https://claude.ai/code/session_01TQLy3Yq6vF4SDpTXdr3Uwi Co-authored-by: Claude <noreply@anthropic.com>
… removed entries The identity-auth.linked-accounts-social checklist item cited setup-nav.contributions.ts#nav_accounts, which no longer resolves (check:platform-checklist: ABSENT SYMBOL). It now cites SysUserDetailPage, whose Linked Accounts tab is the admin surface over sys_account; revision 4, provenance only, no clause change. The sys_account and sys_user_preference list-view comments stop saying a Setup entry names their unscoped view. Claude-Session: https://claude.ai/code/session_01TQLy3Yq6vF4SDpTXdr3Uwi Co-authored-by: Claude <noreply@anthropic.com>
…u entries Claude-Session: https://claude.ai/code/session_01TQLy3Yq6vF4SDpTXdr3Uwi Co-authored-by: Claude <noreply@anthropic.com>
…tup-nav-user-entries
📓 Docs Drift CheckThis PR changes 1 package(s): 37 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 8 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 3 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 698d09ef488c646f01c451a11805acd9e4c75344 && git checkout 698d09ef488c646f01c451a11805acd9e4c75344
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 6d1660dc3b163c30c3a02d34de5cfc219940d958 770f1d617422208c27e886e3afb30cfc9a227a27 && git checkout -B drift-repro 6d1660dc3b163c30c3a02d34de5cfc219940d958 && git merge --no-ff 770f1d617422208c27e886e3afb30cfc9a227a27
node scripts/docs-audit/affected-docs.mjs --json 6d1660dc3b163c30c3a02d34de5cfc219940d958
|
…tup-nav-user-entries
…the admin is served Setup no longer carries nav_accounts or nav_user_preferences, so the folded tree is 8 groups (group_approvals collapses) + 23 platform-objects entries + the MCP entry = 32, measured from the built packages. Both length pins move from 34 to 32, the count comment says 23 contributed entries, and the first case asserts the two removed ids are absent. Claude-Session: https://claude.ai/code/session_01TQLy3Yq6vF4SDpTXdr3Uwi Co-authored-by: Claude <noreply@anthropic.com>
…tup-nav-user-entries
…e and the nav-render checklist step stop naming Setup's two removed entries platform-app-object-entry-views: sys_user_preference leaves the non-vacuity list (no merged entry names it any more) and a new case holds rule (a) for it by name, read from the composition or the identity barrel, the same shape as caller-scoped-first-list-view.test.ts in platform-objects. content/docs/ui/setup-app.mdx: the Advanced row lists OAuth Applications only. platform-core.builtin-apps-nav-render: the Setup detail step drives the User page's Preferences and Linked Accounts tabs instead of two Setup entries; revision 6. The Setup destination count is left for the card's last lander. Claude-Session: https://claude.ai/code/session_01TQLy3Yq6vF4SDpTXdr3Uwi Co-authored-by: Claude <noreply@anthropic.com>
席位核对 · ✅ ACCEPT: PR #23936 at
|
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: thenav_accounts(sys_account, viewall_links) andnav_user_preferences(sys_user_preference, viewall_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:SysUserDetailPagegains a last tab, "Preferences" (en / zh-CN / ja-JP / es-ES, like its siblings). It is arecord:related_listoversys_user_preferenceonuser_id, with the columnskey,created_at,updated_at(theall_preferencesview's columns minususer_id, which is the page's own record), sorted byupdated_atdescending like that view, 25 rows per page with the list's own paging. It authors noaddand noactions. The existing "Linked Accounts" tab oversys_accountis unchanged and serves "Identity links" (measured below), so no second tab is added.en.ts,zh-CN.ts,ja-JP.tsandes-ES.ts, and their recorded digests leave the three*.source-hashes.tstables (an orphan digest redssource-hash.test.ts). Both ids joinDEAD_SETUP_NAV_IDSinsetup-nav-dead-key-tombstone.test.ts, with their reason in its header.packages/platform-objects: the newsetup-nav-per-user-stores.test.tspins (1) no Setup entry this package contributes targets either object, byobjectNameor by a URL; (2) the User page has one related list over each object, keyed onuser_id, accepted byRecordRelatedListProps, and the page parses throughPageSchema; (3) the Preferences list shows a subset of theall_preferencescolumns and authors noaddoractions; (4)unlink_accountis alist_itemaction. Incaller-scoped-first-list-view.test.ts, the non-vacuity control dropssys_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 assertsnav_accountsandnav_user_preferencesare absent. 32 is measured from the built packages: 8 groups (group_approvalsis empty and collapses), plus 23platform-objectsentries, 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_preferenceleaves 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): inplatform-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: itemidentity-auth.linked-accounts-socialcitedsetup-nav.contributions.ts#nav_accounts. It now citessys-user.page.ts#SysUserDetailPage. Revision 3 becomes 4, as provenance only, with no clause change. Without the re-point,pnpm check:platform-checklistexits 1 (ABSENT SYMBOL, measured at2abfd4b4c).packages/platform-objects/src/identity/sys-account.object.tsandsys-user-preference.object.ts: comments only. They no longer say a Setup entry names the object's unscoped view.@objectstack/platform-objectspatch, worded for administrators.No pin was deleted, and no assertion was loosened.
The card's other items
sys_user_preferenceorsys_account.Measured before the change (
origin/mainat497518c3f)nav_accountsandnav_user_preferenceswere atsetup-nav.contributions.tslines 186 and 187, inSETUP_NAV_CONTRIBUTIONS, groupgroup_advanced. Every otherapp: 'setup'contributor (cloud-connection twice, mcp, plugin-approvals, plugin-audit, plugin-auth, plugin-security, plugin-sharing, plugin-webhooks, service-datasource, service-messaging) was grepped forobjectName:, and none names either object.SysUserDetailPagehad "Linked Accounts" oversys_account(relationshipField: 'user_id') and no tab oversys_user_preference. The new tab follows the Sessions and Linked Accounts shape.user_idisField.lookup('sys_user', { required: true }). The record validator refuses an insert without it, and refuses a PATCH that clears it (requiredandrequired_clearedinrecord-validator.ts). A user delete turns the defaultset_nullon a required foreign key intorestrict(engine.ts), so deleting a user cannot detach rows. The one producer, objectui'spackages/data-objectstack/src/userState.ts, writesuser_id: userIdon every insert. Nothing in this repo writes the object server-side, and tenant-wide settings live onsys_setting(ADR-0007). No live database was read, so a row written below the engine (a raw driver write) is outside this reading.daeb77fc84(the files read are byte-identical at objectuimain63d00da). The object grid gates New, row Edit and row Delete throughresolveAffordancewithlistNew,rowEditandrowDelete. The related list gates them throughrelatedNew,relatedRowEditandrelatedRowDelete(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 orby_usergrouping view. Those stay on the object's own list. Its route is unchanged (the console resolves/apps/setup/sys_user_preferencefrom all objects, not from the menu), and a preference record's breadcrumb opens it.sys_account's one row action,unlink_account, haslocations: ['list_item', 'record_header'], and the related-list host offers a child object'slist_itemactions on its rows.caller-scoped-first-list-view.test.ts(control updated, pin (a) kept by name),setup-nav-dead-key-tombstone.test.ts(ledger extended) andsource-hash.test.ts(orphan check, satisfied by removing the digests).console-page-nav-entries.test.tsnames 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_userstays in scope (nav_users, andenable.searchable: true), so searching for a user's name opens their page, where both tabs are. Neither object's rows were ever searchable: both declareenable.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.jsonstill 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) andaudit-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, becauseplugin-auditis outside this claim. No carrier.showViewAllonrecord:related_list(specRecordRelatedListProps, defaulttrue, described as 'Show "View All" link to see all related records') is not read by any objectui renderer.RelatedList.tsxrenders its View all link only when a host passesonViewAll, and no host does (objectui pindaeb77fc84). The new tab setsshowViewAll: truelike its eight siblings on this page. That is the default, so it changes nothing. Filed by the seat as spec(ui):record:related_list.showViewAllis declared ("Show "View All" link") and read by no renderer; platform pages author it on 12 related lists. Enforce or retire #23941.Verification, at head
770f1d617(after mergingorigin/mainat6d1660dc3)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. Ata6f35a120, 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.tsfiles that are not*.dogfood.test.tsalso 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.tsfiles boot example apps and run in CI's Dogfood Regression Gate. None of them reads Setup's menu, and the two that namesys_user_preferenceread it over the data API, which this change does not touch.pnpm --filter @objectstack/platform-objects typecheck: exit 0 ata6f35a120. The three edited or new test files are in thetsconfig.test.jsonprogram (counted with--listFiles).pnpm check:i18n,pnpm check:platform-checklist(822/832 anchors resolved, 10 on the named residual) andpnpm 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:i18nneeded the CLI, andcheck:skill-examplesneededclient-react), then exited 0.check:dual-build-cjs-loadsexited 3 (PREREQUISITE NOT MET: it reads the whole workspace'sdist), so it is NOT MEASURED locally; CI runs it in Build Core, which is green on this head.--ranreconciliation: 94 derived, 93 run, 1 NOT MEASURED, 0 UNRUN.scripts/ablation-replace.mjs(it proves the anchor hit and restores the blob to HEAD):nav_user_preferencesturns 3 tests red;valueto the Preferences columns turns 1 test red;git diff HEADis empty.Generated by Claude Code