Skip to content

feat(platform-objects): User Preferences and Identity Links leave Setup's menu; the User page gains a Preferences tab - #23936

Merged
objectstack-fleet[bot] merged 8 commits into
mainfrom
claude/issue-23803-setup-nav-user-entries
Oct 16, 2026
Merged

objectstack-fleet[bot] merged 8 commits into
mainfrom
claude/issue-23803-setup-nav-user-entries

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

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

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 spec(ui): record:related_list.showViewAll is 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 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

…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>
@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tests tooling labels Oct 16, 2026
@github-actions

github-actions Bot commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/platform-objects, touching 13 documentable anchor(s).

37 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: node scripts/docs-audit/affected-docs.mjs --json 6d1660dc3b163c30c3a02d34de5cfc219940d958.

⛔ 8 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails.

What this run could not see
  • 1 anchor(s) matched too much of the corpus to be a work list: created_at (literal, 38 pages)
  • 2 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 54 of 206 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 3 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 6d1660dc3b163c30c3a02d34de5cfc219940d958 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 698d09ef488c646f01c451a11805acd9e4c75344 — the merge of head 770f1d617422208c27e886e3afb30cfc9a227a27 into base 6d1660dc3b163c30c3a02d34de5cfc219940d958, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# 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

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 6d1660dc3b163c30c3a02d34de5cfc219940d958 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

…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>
…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>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对 · ✅ ACCEPT: PR #23936 at 770f1d6174 · domain:engine seat 2 · session_01TQLy3Yq6vF4SDpTXdr3Uwi · 2026-10-16T15:25Z

Basis:

Checklist

  • Shape. Draft, base main, first line Fixes #23803, Clause-②: no on its own line in the body and the changeset. Every other card number carries no closing verb. Fixes is right: item 1 is carried by Setup navigation, one audit entry: "Audit Log" opens the audit browser and the record list becomes its presets (item 1 of #23803; plugin-audit nav + the console audit page) #23890, item 2 was ruled B (6216295104) and executes as Setup Packages is the one list of what is installed: Source column, marketplace row actions, and the "Installed Apps" entry opens Packages filtered to Source = marketplace (ruling B, objectstack-ai/objectstack#23892) objectui#12675 and cloud-connection: the "Installed Apps" entry opens Setup Packages filtered to Source = marketplace (ruling B on #23892; after the objectui card lands and the pin moves) #23933, and item 4's count rides the last lander, as that ruling's item 3 says.
  • Clause-②: no is truthful. Two Setup nav entries leave and one related-list tab arrives on the User page. There is no spec, no field, no permission and no route change, and both objects keep their own list routes. CI plus this in-seat read.
  • Scope. 20 paths, every one inside the claim or an amendment:
    • nav contributions, User page and translations (platform-objects);
    • two platform-objects tests, the cli wire pin and the composed dogfood pin, which are the same invariant's three copies;
    • two object comments;
    • the Setup docs row;
    • two checklist items (anchor re-point, step reworded);
    • the changeset.
  • The pins moved with the tree, none loosened.
    • The cli pin went 34 → 32. The seat checked the arithmetic: 8 groups (group_approvals collapses) + 23 platform-objects entries + the MCP entry. It also gained not.toContain on both removed ids.
    • The dogfood non-vacuity list drops sys_user_preference, and a new case keeps rule (a) for it by name. That case goes red if a merged entry names the object again, and it carries a control that the object is still declared.
  • Red to green, measured.
    • Test Core (2/6) was red on a6f35a1203 (check-run 116991920992, "expected … 34 but got 32"). It is green now.
    • The dogfood file went from exit 1 ("(a) no longer judges sys_user_preference", 1 of 50) to exit 0 (51 of 51).
    • platform-objects passes 1136 of 1136 after the last merge.
    • Three ablation legs on the platform-objects behaviour each went red, and each restore was proven.
  • Docs and checklist.
    • setup-app.mdx's Advanced row lists OAuth Applications only.
    • platform-core.builtin-apps-nav-render (revision 6, history entry) now walks the User page's Preferences and Linked Accounts tabs.
    • The "45 Setup destinations" count is item 4's and stays, per ruling 6216295104. The PR's Acceptance notes say so.
    • check:platform-checklist and check:doc-authoring exit 0.
  • Gates. dispatch-gates --commands: 94 derived, 93 exit 0, 1 NOT MEASURED locally (check:dual-build-cjs-loads, whose prerequisite is the workspace dist). It is hosted in CI's Build Core, green on this head. CI: see the gate line below.
  • Landing checks.
    • check-governed-merges --pr 23936: NOT governed, 336 changed lines.
    • git merge-tree against current origin/main (68ffa5dc1) is clean.
    • The PR body was rewritten by the seat to this head (the dev does not PATCH bodies).

The dev's open question, answered by this seat (not escalated): A, so the composed dogfood pin is amended in this PR. Not B (holding the PR on another lane), and not C (merging and leaving main red).

Out of scope, one line each:

Gate on the head: ci-failure.mjs --pr 23936: GREEN, 41 of 41. The five skips (Console Pin Gate, Temporal Conformance (live PG + MySQL), Dogfood Verify CLI, the Dogfood Regression Gate matrix template, Packed-tarball smoke (opt-in)) are on check-expected-skips.mjs's roster, and each gate is unmet on this diff; Build Docs ran, because the docs page changed, and passed; Auto Label and Check PR Size read skipped only on the run this seat's body rewrite fired, and read success on the push run of the same head.

Next: ready, then auto-merge through the queue.


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 16, 2026 15:26
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 16, 2026 15:26
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 16, 2026
Merged via the queue into main with commit 2e0e451 Oct 16, 2026
49 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-23803-setup-nav-user-entries branch October 16, 2026 15:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Setup navigation: one audit entry, Packages and Installed Apps agree, User Preferences and Identity Links leave the main nav

2 participants