Skip to content

platform-objects: Setup → Users opens on the "My Profile" view — an admin sees one row (themselves, page size 1) instead of the user list #21960

Description

@objectstack-fleet

Filing gate: ① user-visible UX defect — reach: Setup → Users in the console, measured; separate card under the maintainer's quota exception (provenance below).
Reader: objectstack triage first-touch → the lane owning packages/platform-objects (expected domain:engine) — or objectui if the fix is the Setup nav target; triage decides.
Dedup (semantic search, open + closed, objectstack): "Setup Users list opens on My Profile view showing only the current user, default list view sys_user" → 0 hits.

Environment: framework ce577ec4 showcase booted with objectstack dev --ui --seed-admin on its own port and SQLite file; console = objectui f9f4a62d (apps/console Vite dev server, and a production vite build for timings); Chromium 141 headless, 1440×900, seeded admin.

Repro

  1. Sign in as the seeded admin; open Setup → People & Organization → Users.
  2. Actual: the list opens on the view tab "My Profile" and shows 1 record (the admin). The organisation has 3 members (Setup → Organization → Members (3)). The admin has to discover the "All Users" tab to see the user list.

Where

packages/platform-objects/src/identity/sys-user.object.ts — listViews declares me ("My Profile", filter: id = {current_user_id}, pagination.pageSize: 1) first, before all_users; the console opens the first declared view. The me view exists for the Account app's self-service profile entry (its own comment says so), but the Setup "Users" nav entry lands on it too.

Expected

Setup → Users opens on a view listing users (e.g. all_users); "My Profile" stays reachable where it is meant to be (Account app / its own tab).

Provenance: filed as a separate card on the maintainer's explicit instruction in the dispatching session, which lifts the per-fire filing quota for this batch of seven, verbatim — 「重新思考以上和用户体验相关的问题,哪些需要立卡」 then 「分开立 7 张」. Found during a browser dogfood sweep of the console; companion checklist card objectstack-ai/objectui#11672. Filed by session session_01WEARdKfDHDT7rTuEhLyT1k.


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 6, 2026

    @objectstack-fleet
    ContributorAuthor

    Path: ② the capabilities an end user meets in the app — Setup user administration | 缺项 (no item asserts Setup → Users opens on a user list) | P2

    Triage: first grade — bug · priority:p2 · domain:engine · area:identity · pm:queue. Setup → Users opens on a list of users, and "My Profile" stays the Account app's entry

    Triage seat (objectstack-wide, seat post #6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-06T06:52Z. ⛔ Not a claim, ⛔ not a dispatch.

    Triage: lands in packages/platform-objects/src/identity/sys-user.object.ts (listViews, about :603–:646), and in the Setup navigation entry for Users if that is where the target is named ⇒ domain:engine; rationale: the declaration lists me (one row, the caller, pageSize: 1) first, and the console opens the first declared view.


    Generated by Claude Code

  2. objectstack-fleet commented on Oct 6, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 42 · 2026-10-06T07:30Z
    Session: session_017ErfyP2Rx7XWHJA27QjyUi
    Account: os-project-manager (the seat's linked user, as GET /user answers it; always the card's assignee)
    Branch: claude/issue-21960-setup-users-all-users
    Worktree: objectstack-issue-21960
    Domain: domain:engine
    Seat: domain:engine#1
    Provenance: triage graded this card pm:queue (6011028912). It is the only eligible pm:queue card in this lane, and the lane runs three concurrent claims (the maintainer, verbatim: 「并发3」). #21934 is in contract review, and #21886 waits on the decision box.
    File surface (at origin/main 80f9f7e6ba), per triage's grade and direction 6011028912:

  3. objectstack-fleet commented on Oct 6, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 21960,
    "status": "done",
    "branch": "claude/issue-21960-setup-users-all-users",
    "pr": "#21971",
    "session": "session_017ErfyP2Rx7XWHJA27QjyUi (mode:subagent, the PM session id; identity is the branch)",
    "premise_still_valid": true,
    "summary": "Setup's nav_users entry (packages/platform-objects/src/apps/setup-nav.contributions.ts:77) now sets viewName: 'all_users', the key ObjectNavItemSchema already declares. The console honours it, and before the fix it opened sys_user's first declared view, me (one row, the caller). The me view's comment (sys-user.object.ts:603) no longer says the Account App surfaces it: no navigation item names it, and the Account profile entry is the account:profile_card component. New pins are in setup-users-nav-view.test.ts (7 cases), with a patch changeset. ⛔ No listViews reorder, no view deleted, no spec edit, and no governed path. Draft PR #21971 is at head eb9b315, which merges origin/main 2a22177.",
    "h1_console_reading": "HOLDS, read at objectui 0abd4f9f8769fc4c19ad2f96707684876f74c09f (the .objectui-sha pin), fetched shallow into the scratchpad. The source was read, no browser run. (1) packages/layout/src/NavigationRenderer.tsx resolveHref, case object: precedence recordId, then filters, then viewName. viewName gives OBJECT/view/VIEWNAME; no viewName gives the bare OBJECT path. (2) packages/app-shell/src/views/ObjectView.tsx:2151 sets activeViewId = resolvedViewId || defaultViewId || views[0]. defaultViewId is the isDefault view, which only the object's primary list gets (buildViewTabs promotes it). sys_user declares none, so the bare path opens views[0], the first declared view me. That is the card's observation. (3) packages/core/src/utils/resolve-view-id.ts matches the short 'all_users' against the qualified 'sys_user.all_users'. 'Defaults to "all"' (spec app.zod.ts:432) does NOT describe the runtime when the object declares listViews: 'all' is only the id of the fallback tab built when an object declares no list views. packages/lint/src/lint-view-refs.ts:46-56 already records the fallback as defaultViewId || views[0]. Served path: SchemaRegistry.applyNavContributions (packages/objectql/src/registry.ts:4635) pushes items as authored, and filterNav (packages/rest/src/meta-item-read-gate.ts:774) pushes a passing leaf unchanged, so viewName reaches the wire. nav_notifications already ships viewName 'recent' on the same path.",
    "h2_me_readers": "Navigation items: none. Account's profile entry nav_account_profile is type component, componentRef account:profile_card (account.app.ts), and no Account entry routes to sys_user. objectui at the pin: zero references to the view by name (sys_user.me, view/me, viewName 'me'). account:profile_card is registered in apps/console/src/registerAccountComponents.tsx and renders ProfilePage, which reads useAuth().user and writes via updateUser. Pages: sys-user.page.ts names no list view. Tests: none. Non-runtime references: the tab label _views.me in the four generated translation bundles, and the spec ObjectNavItemSchema docblock (app.zod.ts:407) citing sys_user.me as filter-vocabulary example. So me is reached only as a view-switcher tab, and as the default tab of any route naming no view. Triage pin 2 is held by the component and pinned there: the first Account entry is account:profile_card, and no Account entry targets sys_user. The old comment was wrong at main and is corrected; me is kept.",
    "tests": "At HEAD eb9b315: pnpm --filter @objectstack/platform-objects exec vitest run --maxWorkers=2 gives 'Test Files 61 passed (61) / Tests 967 passed (967)', lock VERDICT command-exit 0. pnpm --filter @objectstack/platform-objects typecheck exits 0 with 'check:test-typecheck: OK ... 1 file(s) / 3 error(s) / 2 pinned signature(s) held'. That ledgered debt is in feature-gate-guard.test.ts, not the new file. tsc -p tsconfig.test.json --listFiles lists setup-users-nav-view.test.ts once. Pin file alone: 7 passed. REVERSE VERIFICATION on committed 6639eb4 ran through node scripts/ablation-replace.mjs --file packages/platform-objects/src/apps/setup-nav.contributions.ts --anchor \"viewName: 'all_users', \" --delete -- pnpm --filter @objectstack/platform-objects exec vitest run --maxWorkers=2 src/apps/setup-users-nav-view.test.ts. The tool printed 'ok mutation landed: anchor 1 -> 0, blob 72a3adcc1066 -> 7c70ade946a9', then 'Tests 4 failed | 3 passed (7)': the 4 viewName cases went red, and the recordId/filters case and both Account cases stayed green, as expected. It then printed 'ok restored: blob == HEAD (72a3adcc1066) and git diff HEAD is empty', and a separate git status --porcelain read empty. The subject is imported by relative src path, not through the package exports, so no dist/ sits on the path and dist-preflight does not apply. A first attempt (replace mode) was refused before running because the replacement text sat inside the anchor ('a rise of 0, not the declared 1'). It was a no-op, it was restored, and its result is not counted. ESLint narrowed to the 3 touched TS files (--format json): 3 files, 0 errors, 0 warnings. Population: the eslint.config.mjs packages/**/*.{ts,tsx,mts,cts} blocks, none reported ignored. Invariance: the config enables no type-aware linting ('no parserOptions.project', eslint.config.mjs:326-328). Repo-wide pnpm lint is CI's.",
    "gates": "All run at HEAD eb9b315, after the spec rebuild and a full turbo run build --concurrency=2 (72 tasks). DERIVED: dispatch-gates --commands (no paths) gave 63; 63 ran, and all exited 0. dispatch-gates --ran read 'Run reconciliation — 63 derived, 63 run, 0 NOT-MEASURED, 0 UNRUN' (a DERIVED zero, every line exit-coded). 14 are new against the dispatch list: check-adr-0087-registration (base and self-test), check-empty-changeset (base and self-test), release-rehearsal-clone --self-test, release-pending-publish --self-test, check:engine-double-contract, check:objectql-double-limit, check:objectui-changeset, check:pm-changeset-deadline-census, check:query-options-erasure, check:type-check-coverage, check:type-check-debt and check:where-matcher. ARTIFACT-ROSTER block: 55 families; 55 ran, and 52 exited 0. The other 3 (check-closing-target-claim.mjs, check-partof-closing-keyword.mjs, check-single-claim-paths.mjs) answered NOT WIRED (exit 2) with no PR context. Re-run with PR context after the PR opened (PR_NUMBER=21971), all three are green: '✓ check:closing-target-claim: PR #21971 closes #21960, and each carries a Claim: whose Branch: line names claude/issue-21960-setup-users-all-users', '✓ check:single-claim-paths: PR #21971 modifies none of the 1 declared at-most-one-writer path(s)', and '✓ check:partof-closing-keyword' (run on PR_BODY). Two roster rows (check:pm-write-pace, check:single-claim-paths) were missed by my first extraction and then run: both exit 0. SYMBOL ANCHORS: check:adr-symbol-anchors, check:scripts-symbol-anchors, check:spec-docblock-symbol-anchors and check:adr-anchors all exit 0. PREREQUISITE REFUSALS, then reruns: check:dual-build-cjs-loads, check:i18n, check:lean-entry-closure and check:published-readme-exports first exited 3 (no dist/). After the full build all four exited 0 ('check-i18n-bundles: OK (9 package(s) — all bundles in sync...)'). dts-closure, published-files and sourcemap-no-sources-content were re-run post-build: exit 0. check:type-check-debt was first killed at my runner's 420 s cap (exit 124) while refreshing its closure. The rerun exited 0: 'check-type-check-coverage --re-measure: OK — 1 ledger entr(ies) re-measured ... none above its recorded number'. Generated catalogs and count pins: no regeneration was needed. check:i18n is in sync because viewName is not a translated string, and the spec build left the tree clean.",
    "line_budget": "n/a",
    "files_changed": [
    ".changeset/21960-setup-users-all-users.md (new, @objectstack/platform-objects patch, Clause-②: no)",
    "packages/platform-objects/src/apps/setup-nav.contributions.ts (+6/-1: nav_users viewName all_users plus why-comment)",
    "packages/platform-objects/src/identity/sys-user.object.ts (comment only, me view)",
    "packages/platform-objects/src/apps/setup-users-nav-view.test.ts (new, 7 pins)"
    ],
    "deviations": [
    "Commit trailers use AGENTS.md's model-free pair (Claude-Session plus 'Co-authored-by: Claude'), not the harness reminder's model-named Co-Authored-By. The PR body ends with the AGENTS.md session-URL footer, not the harness reminder's form. Both follow os-dev.md: harness attribution yields to this contract.",
    "origin/main was merged at 2a22177 before the PR opened. It then advanced to 4c49150 (spec api test titles plus scripts/partition-test-shards.mjs, no overlap with this diff), and it was not re-merged, to keep the gate union on the PR head. CI validates the merge ref.",
    "objectui was read from a shallow fetch of the pinned sha into the scratchpad (no sibling checkout exists in this container).",
    "No dogfood door case was added: the served nav path passes the item through verbatim (two sites named in h1), and the data half is unchanged. The metadata pin holds the declaration."
    ],
    "mcp_calls": "0 — no MCP GitHub calls.",
    "api_writes": "3 REST writes, each one repository_dispatch through the scripts/pm fleet-write relay as objectstack-fleet[bot]: (1) pr_create via POST /repos/objectstack-ai/objectstack/dispatches, which the executor turned into POST /repos/objectstack-ai/objectstack/pulls; PR #21971 draft, read-back 9812 bytes sent and 9812 stored, identical. (2) label-write --assign os-project-manager via POST /dispatches, then POST /repos//issues/21971/assignees; read-back MATCHES. (3) this os-dev-report comment via POST /dispatches, then POST /repos//issues/21960/comments. Not REST: three git pushes of the branch (empty probe, the fix commit 6639eb4, merge eb9b315). No label written: the dispatch named none, and skip-changeset does not apply because the diff publishes.",
    "open_questions": [
    {
    "question": "This PR closes the Setup nav door only. sys_user still opens me on every route that names no view: objectui AppHeader links the object breadcrumb (AppHeader.tsx:434) and the object switcher (:367) to the bare /apps/setup/sys_user. And because a viewName item scores only on its own view (NavigationRenderer.tsx:999, resolveActiveNavItem :1030), nav_users is no longer highlighted on a user's record page or on the other tabs. Should the default view of sys_user (and of the six H3 objects) also change? The dispatch reserved the declaration-order route (H2 ⛔), so it is not taken here.",
    "options": [
    "A — Keep this PR as is. File one family-closure card for 'Setup object entries whose object's first declared list view is scoped to the caller'. It covers the residual bare-object doors of sys_user and the six H3 entries, and decides per object whether to name a view on each entry or to move the caller-scoped view off first place. objectui separately makes a viewName item weak-claim its object's sub-routes. Cost: the residual doors stay until the family card lands.",
    "B — Also move all_users ahead of me in sys_user.listViews, in this PR. Every bare door lands on the list now, and me stays a tab with no reader lost (H2). Cost: it reopens the H2 ⛔, and the highlight gap stays while viewName is kept.",
    "C — Reorder only, and drop viewName. That fixes every door and keeps the bare-item highlight. Cost: the Users entry again depends on an implicit order that a future edit can silently flip, which the explicit key pins."
    ],
    "recommendation": "A. Business need: me has no named reader (H2), so the remaining harm is the bare doors. That is the same family as the six H3 entries and is better decided once for all of them than piecemeal. Long-term: the explicit viewName is the contract-first spelling and survives a reorder. The highlight gap is a renderer gap every viewName entry already has, and it belongs in objectui. Against AI error: an implicit 'first declared view is the default' rule is the trap here, and naming the view is the declared way out of it. Startup scope: no new key and no new gate. The family card carries the rest."
    }
    ],
    "out_of_scope_findings": [
    "class: a · family 'Setup object entry opens a caller-scoped first list view' (H3), one family-closure card rather than single cards; members follow. reach: named producer — the Setup nav contributions ship in every deployment, and with no viewName the console opens the first declared view (objectui ObjectView.tsx:2151 at the pin; source read, not browser-measured). Dedupe words: Setup nav entry opens mine view first, caller-scoped default list view, admin sees only own rows",
    "member · nav_api_keys (setup-nav.contributions.ts:108) → sys_api_key: first view mine (user_id = {current_user_id}, pageSize 50). The Account entry names viewName 'mine' explicitly (account.app.ts:188-189), so the Setup default serves no Account reader.",
    "member · nav_sessions (setup-nav.contributions.ts:145) → sys_session: first view mine (user_id = {current_user_id}, pageSize 50). The Account entry names 'mine' explicitly (account.app.ts:168-169).",
    "member · nav_oauth_apps (setup-nav.contributions.ts:154) → sys_oauth_application: first view mine (pageSize 50). The Account entry names 'mine' explicitly (account.app.ts:197-198).",
    "member · nav_accounts (setup-nav.contributions.ts:185) → sys_account: first view mine (pageSize 50). ⚠ The Account app's nav_account_linked (account.app.ts:157) names NO view and relies on this same order, so a fix must name 'mine' there first.",
    "member · nav_user_preferences (setup-nav.contributions.ts:186) → sys_user_preference: first view mine (pageSize 100).",
    "member · nav_record_shares (packages/plugins/plugin-sharing/src/sharing-plugin.ts:591) → sys_record_share: first view granted_to_me (recipient_id = {current_user_id}, pageSize 50).",
    "member · the residual sys_user doors: objectui AppHeader's object breadcrumb (AppHeader.tsx:434) and object switcher (:367) link to the bare object path, which still opens me after this PR (sys-user.object.ts:603 declares it first).",
    "class: b · Seam: spec:ObjectNavItemSchema.viewName → renderer:objectui ObjectView (activeViewId, ObjectView.tsx:2151). The contract reads, verbatim (packages/spec/src/ui/app.zod.ts:432): 'Default list view to open. Defaults to "all". Ignored when recordId is set.' The runtime opens the isDefault/primary list or else the FIRST declared list view; 'all' exists only as the fallback tab of an object with no listViews. lint-view-refs.ts:46-56 quotes the same text and records the real fallback. reach: the card's own measured repro — Setup → Users with no viewName opened 'My Profile', not all records (browser, objectui f9f4a62d). Fixing it needs a spec describe edit (out of this dispatch: ⛔ no packages/spec/src edit). Dedupe words: viewName describe Defaults to all, nav item default list view first declared, ObjectNavItemSchema viewName default",
    "carrier: 承接者:无 · noted, not filed — objectui resolveActiveNavItem/itemMatchScore (NavigationRenderer.tsx:999) scores a viewName item 0 on every other sub-route of its object, so a lone viewName entry (now nav_users; already nav_notifications and the Account mine entries) is not highlighted on record pages or other tabs. A bare item weak-claims them. Read from source at the pin, not browser-measured."
    ]
    }


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 6, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed: PR #21971 → f76c6221ac on main. It merged through the merge queue at 2026-10-06T09:27Z, after entering the queue at 2026-10-06T08:51Z. Verified at 2026-10-06T09:27Z. domain:engine#1 · session_017ErfyP2Rx7XWHJA27QjyUi.


    Generated by Claude Code

  5. added 2 commits that reference this issue on Oct 7, 2026
    f76c622
    1c563af
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

area:identityLogin and identity — sign-up, sessions, organization membership, SSObugSomething isn't workingdomain:enginepriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions