Skip to content

[finding] single-org users have no UI path to the workspace manage pages — the ?manage=1 avatar-menu entry the code describes does not exist #8096

Description

@baozhoutao

Recording an observation for the platform to rule on.

A user who belongs to exactly one organization appears to have no UI path to the workspace management pages (/organizations/<slug>/members · /invitations · /settings). Three mechanisms that are each individually reasonable close every door at once:

  1. /organizations auto-skips. Deliberate, and documented as such:

    UX P0-1: auto-skip the picker when the user belongs to exactly one organization. […] We still mount the page (so direct navigation to /organizations works) but redirect once orgs have finished loading.
    (packages/app-shell/src/console/organizations/OrganizationsPage.tsx)

    Confirmed empirically: with one org, GET /_console/organizations lands on the last app page; after creating a second org, the same URL renders the picker.

  2. The workspace switcher is hidden with a single org. Verified: the top bar shows no workspace control until the user has ≥2 organizations, at which point the switcher (with its Manage members item) appears. Sensible on its own — but it is the only entry point to the manage pages.

  3. The avatar menu has no "My Organizations" item. The comment right above the auto-skip explains that this is what should reopen the picker:

    Two deliberate ways to reach this page (vs the auto-skipping post-login redirect): ?manage=1 (avatar menu "My Organizations") shows the picker; ?create=1 (avatar menu "Create workspace") additionally opens the create dialog directly. Both suppress the single-org auto-skip below so a single-org user can actually reach "New organization" / the dialog.

    Observed avatar menu (zh, both a single-org and a two-org user): 个人资料 / 创建工作区 / 主题 / 语言 / 注销. 创建工作区 is the ?create=1 entry and works; there is no ?manage=1 entry.

Net effect: a single-org owner cannot reach Members / Invitations / Organization settings without hand-typing /_console/organizations?manage=1. Inviting still works from Setup → Users → Invite user, so the workspace is not unusable — but role changes, invitation cancellation, renaming the org and deleting it are all only in those pages.

The decision

Either restore the ?manage=1 avatar-menu item the comment describes, or show the workspace switcher unconditionally (a one-org switcher is a bit redundant but carries Manage members), or point Setup at the manage pages. If single-org users are intentionally not meant to reach these pages at all, the stale comment should go instead.

Environment

Local dev server http://localhost:8080, multi-org enabled, zh locale, observed 2026-08-12. The server was started by the maintainer and its exact commit is not verified; the source quoted is the current objectui main checkout.

Activity

  1. baozhoutao commented on Aug 12, 2026

    @baozhoutao
    ContributorAuthor

    Bugs filed from the same multi-org walkthrough: #8090, #8091, #8092, #8093, #8094.

  2. added
    bugSomething isn't working
    and removed on Aug 12, 2026
  3. added theissue type on Aug 12, 2026
  4. hotlong commented on Aug 12, 2026

    @hotlong
    Contributor

    Finding-grading round: promoted to pm:queue (lane repo:objectui, type Bug — lands in objectui packages/app-shell).

    All three doors re-verified shut in objectui origin/main: WorkspaceSwitcher.tsx:87 returns null for orgList.length <= 1, AppHeader.tsx:896-907 kept only ?create=1 (comment claims "My Organizations" moved to the switcher — the switcher single-org users never see), OrganizationsPage.tsx still documents the ?manage=1 avatar entry that no longer exists. Narrowest fix is what the code's own comment declares should exist: restore the avatar-menu "My Organizations" → /organizations?manage=1. Invariant-restoring, no product call. Siblings: walkthrough cards #8090–#8094.


    Generated by Claude Code

  5. self-assigned this
    on Aug 14, 2026
  6. yinlianghui commented on Aug 14, 2026

    @yinlianghui
    Collaborator

    Claim: PM loop round 1
    Session: session_01RnQd8iMMUwXQEV1crFmQiQ
    Branch: claude/issue-8096-manage-entry
    Worktree: objectui-issue-8096
    Domain: repo:objectui
    File surface: packages/app-shell/src/layout/AppHeader.tsx (avatar menu) + packages/app-shell/src/console/organizations/OrganizationsPage.tsx (stale-comment reconciliation only) + app-shell locale resources if the menu label key does not already exist + matching tests (stop on breach; explain in the report)
    Container & model: S–M, mode:subagent, model: opus
    Serial constraints cleared: in-flight this batch: #8500 declares packages/app-shell/src/views/ (disjoint files, same package — no barrel edits expected on either side); #8282/#8270 touch the same console/organizations + locale surfaces and are deliberately held for a later round (serial); no open dev PR touches packages/app-shell/src/layout/ (checked origin/main log + open PR list this round).


    Generated by Claude Code

  7. yinlianghui commented on Aug 14, 2026

    @yinlianghui
    Collaborator
    {
      "issue": 8096,
      "status": "done",
      "branch": "claude/issue-8096-manage-entry",
      "pr": "https://github.com/objectstack-ai/objectui/pull/4638",
      "premise_still_valid": true,
      "summary": "Restored the avatar-menu \"My Workspaces\" entry navigating to /organizations?manage=1 in packages/app-shell/src/layout/AppHeader.tsx — the narrowest declared fix. WorkspaceSwitcher.tsx was not touched and no Setup-side links were added, per the 裁决. All three doors re-verified shut on origin/main (5bf5d2cb6) before implementing. One premise sub-detail is WRONG and the PM should note it: `?manage=1` does NOT suppress the single-org auto-skip as OrganizationsPage:42's comment claims — the manage-mode branch at :130 RE-TARGETS the auto-skip at /organizations/<slug>/members instead. That is better than the card assumed, not worse: the restored entry deep-links a single-org user straight into member management rather than showing a pointless one-item picker, so it does not dead-end and the 裁决's stop-condition was not triggered. Route confirmed end-to-end (apps/console/src/App.tsx:298-307 → members/invitations/settings). Reused the existing `organizations.mine` key — already shipped in all ten locale packs and left dormant when the entry was removed — so NO locale files were touched. Entry deliberately not gated on `multiOrgDisabled` (that flag governs CREATE; where self-service creation is off this entry is the only door).",
      "tests": "All at HEAD bc89db3 (verification run followed the final commit; nothing pushed after). Repo convention followed per objectui AGENTS.md §'怎么跑测试': repo-root `pnpm exec vitest run <path>`, NOT `pnpm --filter <pkg> test` (that form is a documented silent-false-green trap here — it runs apps/console's 22 files and reports green). Build closure first: `pnpm --workspace-concurrency=2 --filter '@object-ui/app-shell^...' build` → exit 0.\n\n(1) New test packages/app-shell/src/layout/__tests__/AppHeader.myOrganizations.test.tsx, 5 cases → with WorkspaceSwitcher.test.tsx + OrganizationsPage.test.tsx: 'Test Files 3 passed (3) / Tests 11 passed (11)'.\n(2) REVERSE VERIFICATION, direction predicted before running: all 5 cases assert on the `header-my-organizations` testid, so reverting AppHeader to origin/main must fail all 5 — not 1, not 4. Observed exactly 'Test Files 1 failed (1) / Tests 5 failed (5)'. Fix was COMMITTED first, so restore was `git checkout claude/issue-8096-manage-entry -- <path>`, confirmed byte-identical via `git diff --quiet HEAD`.\n(3) Blast radius `vitest run packages/app-shell/src/layout/ packages/app-shell/src/console/organizations/` → 'Test Files 36 passed (36) / Tests 255 passed (255)'.\n(4) `pnpm --filter @object-ui/app-shell type-check` (tsc --noEmit && tsc -p tsconfig.test.json) → exit 0.\n(5) eslint on the 3 changed files → 0 errors, 44 warnings (all `any`/unused-destructure inside the test's mock factories, identical in kind to sibling AppHeader.inboxVariant.test.tsx).\n(6) Gates re-derived against actual changed paths, beyond the ones named in dispatch: check:control-bytes OK (4174 files), check:i18n-keys OK (confirms `organizations.mine` resolves against the en pack AND the inline defaultValue matches its value exactly — this is what validates the key reuse), check:i18n-drift OK (0 en values changed), check-changeset-presence OK (1 changeset for 2 changed source files of a released package). check:i18n-keys/i18n-drift were NOT in the dispatch gate list; they are implicated because the change adds a t() call site, and both are green.\n\nNOTE — one self-inflicted lap worth recording: the new test first died with a V8 heap OOM ('Worker exited unexpectedly'), not an assertion failure. Cause was in my test harness, not the fix: the mocked `useAuth` returned a fresh `getAuthConfig` arrow per call, and BOTH AppHeader and WorkspaceSwitcher have `useEffect(..., [getAuthConfig])` ending in setState — new identity every render ⇒ effect ⇒ setState ⇒ render, until the heap died. Same defect via a per-call `useAdapter` object. Fixed by hoisting both to stable module-level identities (the sibling inbox test dodges this by passing `getAuthConfig: undefined`). Recorded because the failure mode reads like an environment problem and is not one.",
      "open_questions": [],
      "out_of_scope_findings": [
        "filed as #8669: the `organizations.*` locale family is half-renamed — 8 of 10 packs (ar/de/es/fr/ja/ko/pt/ru) followed the workspace rename for `create` ONLY, so e.g. de now renders 'Meine Organisationen' two lines above 'Workspace erstellen' in the same dropdown. Newly visible because this PR revives the dormant `organizations.mine` key. Labeled `finding` (observation-class), no pm:queue. Existing gates are structurally blind to it: key-parity sees every key present, i18n-drift only follows en changes, i18n-keys only compares against en."
      ]
    }

    Lock/scope notes for the PM:

    • Shared verification lock contention was significant this round. /tmp/os-heavy-verify.lock was held ~30 min by PID 12098 running an unfiltered full-repo suite (flock … -c 'pnpm exec vitest run --maxWorkers=2', no path filter). I queued through three bounded -w windows (~27 min total), spending each interval on lock-free work (changeset, cheap gates, commit, PR body), then batched every remaining heavy step into a single acquisition. Not blocked in the end, but if full-repo runs under the lock are routine this round's card latency was mostly queueing, not work.
    • Sibling-agent boundary respected: packages/app-shell/src/views/ and the barrel src/index.ts were not touched. Final diff is 4 files: layout/AppHeader.tsx, console/organizations/OrganizationsPage.tsx (comments only), the new test, and .changeset/tidy-donkeys-attack.md.
    • The tests label on PR feat(spec)!: 双源 C3 收敛 — 通知语汇归 ./api,./ui 与 ./system 侧死删 (#4610) #4638 was applied by another actor, not me — left as-is per ground rule 2. No skip-changeset label is needed: this is a user-visible change in a published package and carries a changeset.
    • CI was still converging (mergeable_state: unstable) at report time; per the 2026-08-10 ruling the convergence wait is the PM's.

    Generated by Claude Code


    Generated by Claude Code

  8. yinlianghui commented on Aug 14, 2026

    @yinlianghui
    Collaborator

    ACCEPT — PR objectstack-ai/objectui#4638 (reviewer of record: seat session session_01RnQd8iMMUwXQEV1crFmQiQ).

    Verified against GitHub: diff is 4 files exactly as claimed (AppHeader.tsx, OrganizationsPage.tsx comments-only, the new 5-case test, one changeset); the declared narrowest fix and nothing else — WorkspaceSwitcher untouched, no Setup links, no locale files (existing organizations.mine key reused, validated by check:i18n-keys). Reverse verification predicted 5/5 RED on revert and measured exactly that. Premise sub-correction accepted and publicly recorded: ?manage=1 re-targets the single-org auto-skip at /organizations/<slug>/members (a deep-link into member management) rather than "showing the picker" as the code comment claimed — better than the card assumed; the restored entry does not dead-end. Changeset present (user-visible change in a published package). The earlier shard-3 CI red was the base-branch failure (objectui#4642), fixed and merged — the PR now goes ready + auto-merge through the queue. Fixes first line is correct: merge closes this card.

    Out-of-scope finding #8669 (half-renamed organizations.* locale family) verified filed, unassigned, observation-class — enters this seat's grading pool.


    Generated by Claude Code

  9. added a commit that references this issue on Aug 17, 2026
    67b0119
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions