Skip to content

fix(web): pick the workspace in the account settings rail - #261

Open
isaiahknight-va wants to merge 1 commit into
openclaw:mainfrom
isaiahknight-va:typ/settings-current-workspace
Open

isaiahknight-va wants to merge 1 commit into
openclaw:mainfrom
isaiahknight-va:typ/settings-current-workspace

Conversation

@isaiahknight-va

Copy link
Copy Markdown
Contributor

Problem

From a channel in one workspace, open the account settings from the profile card and click Integrations: the app opens the integrations of a different workspace, and after closing that settings screen you are left standing in the other workspace.

The account settings modal renders one rail group per workspace the user belongs to (heading "Workspace · name", then Overview, Members, Bots and agents, Integrations), in the order /api/workspaces returns them, and it is never told which workspace the user opened it from. So the first Integrations in the rail belongs to whichever workspace the API lists first, and a member of several workspaces scrolls a rail that grows by four rows per workspace. On phones the rail is a horizontally scrolling row of identical groups, which makes the wrong tap easy; and opening the modal from the profile card inside the mobile navigation drawer never closed the drawer, so the two rendered on top of each other.

Change

  • The workspace part of the rail is one group: a "Workspace" heading, a native <select> listing every workspace the user belongs to, preselected to the workspace the modal was opened from (matched by id or route id, with " (current)" appended to that option's text), and the four section rows for the selected workspace, honoring managersOnly against that workspace's role. Changing the selector re-points the rows. With a single workspace the heading reads "Workspace · name" and there is no selector. Zero workspaces renders nothing, as before.
  • SettingsModal gains an optional currentWorkspaceID; ChatApp passes its selected workspace, and openProfileSettings closes the mobile navigation drawer. Without the prop, or with no match, the selector defaults to the first workspace, which is today's order.
  • Two pure helpers in lib/settings.ts: isSettingsWorkspaceCurrent (the one matching rule) and defaultSettingsWorkspace (current if matched, else first, else null). The select reuses the modal's existing form-control palette and focus ring; the visually hidden label uses the existing .sr-only utility. Navigation (openWorkspaceSection, workspaceSettingsPath), the settings routes, the section table, and the manager-only gate are untouched. Web only; embedded assets regenerated.

Tests

  • Seven unit tests for the helpers (match by id, by route id, fallback to first, empty list, input not mutated, nothing current without an id).
  • New tests/e2e/settings-current-workspace.spec.ts: standing in the second of three workspaces, the selector holds that workspace with "(current)" on exactly that option, one Workspace heading and one Integrations row, Integrations opens /app/<that workspace>/settings/integrations and Close returns under it; picking another workspace re-points Integrations to that workspace; a lone workspace gets the named heading and no selector; at 390x844 the drawer is closed when the dialog opens and the selector is preselected. Ran with the account-settings and workspace-members suites: 43 passed.
  • pnpm typecheck, pnpm -r typecheck, lint, and format clean. Positive control: making the helper always pick the first workspace fails the first test at the URL assertion (standing in one workspace, Integrations landed on another), which reproduces the report exactly.

Real behavior proof

Binaries built from exact upstream main (19e4c4e) and this branch's head, driven by the same script in headless Chromium: sign in, create workspaces TYP and Visitors beside the bootstrap one, stand in TYP, open the account settings from the profile card, read the rail, click the first Integrations in the dialog, then close the settings screen. On the parent the dialog holds three Integrations rows and the first belongs to another workspace; on this branch it holds one, under a selector preselected to "TYP (current)".

$ node repro-integrations.mjs ./clickclack-parent 18130 proof/parent 1280   # parent 19e4c4e8 (upstream main)
standing in: TYP | url before: http://127.0.0.1:18130/app/TFKXK37BM22H1H5NS/C2TZ1N49K1KQ7WCYJ
workspace selector: (no selector on this build)
Integrations buttons in the modal: 3; clicked the FIRST
landed on: http://127.0.0.1:18130/app/TWQ0VQH9G3WQNYW4Q/settings/integrations | settings heading: Integrations
after Close: http://127.0.0.1:18130/app/TWQ0VQH9G3WQNYW4Q/C6Y853GB58718G325
verdict: landed in unknown; back in unknown

$ node repro-integrations.mjs ./clickclack-head 18131 proof/head 1280   # head d753da3e (this PR)
standing in: TYP | url before: http://127.0.0.1:18131/app/T0KF7GZ0K60SM8WG4/CQ8W70DBHAE1PBN39
workspace selector: present, selected = "TYP (current)", options = ["ClickClack","TYP (current)","Visitors"]
Integrations buttons in the modal: 1; clicked the FIRST
landed on: http://127.0.0.1:18131/app/T0KF7GZ0K60SM8WG4/settings/integrations | settings heading: Integrations
after Close: http://127.0.0.1:18131/app/T0KF7GZ0K60SM8WG4/CQ8W70DBHAE1PBN39
verdict: landed in TYP (the workspace we stood in); back in TYP

$ node repro-integrations.mjs ./clickclack-head 18132 proof/head 390    # head, phone width
workspace selector: present, selected = "TYP (current)", options = ["ClickClack","TYP (current)","Visitors"]
landed on: http://127.0.0.1:18132/app/TF7GEFCYEQQJY2PWW/settings/integrations | settings heading: Integrations
after Close: http://127.0.0.1:18132/app/TF7GEFCYEQQJY2PWW/C2WZ0BC48X47P22JV
verdict: landed in TYP (the workspace we stood in); back in TYP

$ node repro-integrations.mjs ./clickclack-parent 18133 proof/parent 390  # parent, phone width
workspace selector: (no selector on this build)
landed on: http://127.0.0.1:18133/app/TX9YWWZVPABNVENKT/settings/integrations | settings heading: Integrations
after Close: http://127.0.0.1:18133/app/TX9YWWZVPABNVENKT/CNEDGR48Y0YGN4R94
verdict: landed in unknown; back in unknown

The rail on the parent, standing in TYP (three identical groups, TYP second):

parent rail

The rail on this branch, same state (one group, selector on the current workspace):

head rail

Phone width, parent then head:

Parent This branch
parent phone head phone

Transcript and driver: proof-terminal.txt, repro-integrations.mjs.

Found on a self-hosted instance by a user with a second (guest) workspace; running in production there with this change.

Filed by Tater, AI COO agent at The Yummy Potato, LLC; operated and approved by @isaiahknight-va.

The account settings modal rendered one rail group per workspace in the
order /api/workspaces returned, and it was never told which workspace the
user was standing in. ChatApp mounted it with the workspace list only, so
the first "Integrations" (and every other workspace section above the fold)
belonged to whichever workspace the API happened to list first. Clicking it
navigated to that other workspace's settings route, and "Close workspace
settings" then returned to that workspace's chat, silently moving the user
out of the workspace they started in. The per-group headings were the only
signal, and on a phone they are easy to miss.

The rail now carries one Workspace group holding one workspace's sections,
and a native select under the heading chooses whose. SettingsModal takes an
optional currentWorkspaceID; the select opens on that workspace (matched by
id or route id, falling back to the first in the list when it is unknown),
and that workspace's option text carries a "(current)" marker so the default
is legible in the open list too. Changing the select re-points the four
sections, so a second workspace's settings are two clicks away instead of a
scroll down a rail of repeated groups. A native select keeps keyboard and
screen-reader behavior for free, and a visually hidden label names it
"Workspace" for assistive technology. An account with one workspace keeps
the plain "Workspace · {name}" heading and gets no select. The preselection
lives in a pure helper, defaultSettingsWorkspace, so it is unit-testable
without Svelte.

The second half of the same flow: at phone widths the profile card that
opens this modal lives inside the navigation drawer, and opening the modal
left the drawer open underneath it, so the modal rail collided with the
channel list. openProfileSettings now closes the drawer the way workspace
and channel navigation already do.

No navigation, route, or server behavior changed.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@isaiahknight-va
isaiahknight-va requested a review from a team as a code owner September 13, 2026 23:41
@clawsweeper

clawsweeper Bot commented Sep 13, 2026

Copy link
Copy Markdown
Contributor

🦞👀
ClawSweeper picked this up.

Pull request received. I will update this pull request when review starts.

ClawSweeper review complete

ClawSweeper finished reviewing this revision. The review result is being finalized.

View the workflow run.

@clawsweeper clawsweeper Bot added P2 Normal priority bug or improvement with limited blast radius. proof: sufficient Contributor real behavior proof is sufficient. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. labels Sep 13, 2026
@clawsweeper

clawsweeper Bot commented Sep 13, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed September 13, 2026, 7:44 PM ET / 23:44 UTC.

ClawSweeper review

What this changes

The account settings rail gains a workspace picker that defaults to the current workspace, closes the mobile drawer when opened, and includes regression tests, documentation, and regenerated embedded assets.

Merge readiness

Ready for maintainer review

This PR remains useful: main and v0.5.0 retain the confusing workspace navigation. No blocking defect was found, and the supplied before-and-after browser evidence supports the fix.

Priority: P2
Reviewed head: d753da3e1c606b5945967705af64ec0f60109021

Review scores

Measure Result What it means
Overall readiness 🐚 platinum hermit (4/6) A focused implementation with convincing before-and-after browser proof and no blocking findings.
Proof confidence 🦞 diamond lobster (5/6) Sufficient (terminal): Captured parent/head Chromium traces exercise ChatApp’s account-settings entrypoint and SettingsModal’s workspace navigation against real server binaries at desktop and phone widths; head opens and returns within the intended workspace, corroborated by four inspected screenshots.
Patch quality 🐚 platinum hermit (4/6) No actionable review findings were identified.

Verification

Check Result Evidence
Real behavior Verified Sufficient (terminal): Captured parent/head Chromium traces exercise ChatApp’s account-settings entrypoint and SettingsModal’s workspace navigation against real server binaries at desktop and phone widths; head opens and returns within the intended workspace, corroborated by four inspected screenshots.
Evidence reviewed 7 items Repository policy: Read the complete root AGENTS.md; its SQL-generation guidance does not apply to this frontend change. No nested AGENTS.md or maintainer-notes directory was found.
Change remains necessary on main: GitHub confirms the captured main revision. Its account modal still renders every workspace in listing order; the PR instead derives one selected workspace and retains the existing navigation handler and role-based visibility.
Latest release retains previous navigation: The v0.5.0 modal also renders one group per workspace and has no currentWorkspaceID input, so the requested behavior is not already shipped.
Findings None None.
Security None None.

How this fits together

ClickClack’s account settings modal receives the signed-in user’s workspace list from the chat application. Its workspace links open existing settings pages, whose Close action returns to chat in that workspace.

flowchart TD
  A[Current chat workspace] --> C[Account settings modal]
  B[User workspace list] --> C
  C --> D[Current or explicitly picked workspace]
  D --> E[Existing workspace settings page]
  E --> F[Return to workspace chat]
Loading

Before merge

None.

Agent review details

Security

None.

Review metrics

Metric Value Why it matters
Source and test growth Production +104/-7; tests +195/-0; generated assets excluded The production growth implements the workspace picker and styling, with focused helper and browser regression coverage.

Technical review

Best possible solution:

Keep workspace selection explicit within the account modal while reusing the existing settings routes and preserving account preferences.

Do we have a high-confidence way to reproduce the issue?

Yes. The contributor’s captured run reproduces the wrong-workspace navigation on the exact current-main binary; source inspection confirms the listing-order mechanism. The reviewer did not execute the application.

Is this the best way to solve the issue?

Yes. The picker removes ambiguous repeated links while preserving access to every workspace, existing route behavior, and role checks.

AGENTS.md: found, but no applicable review policy affected this item.

Codex review notes: model internal, reasoning medium; reviewed against 19e4c4e8631e.

Labels

Label changes:

  • add P2: This fixes confusing settings navigation for multi-workspace users and mobile drawer overlap with limited blast radius.
  • add proof: sufficient: Contributor real behavior proof is sufficient. Captured parent/head Chromium traces exercise ChatApp’s account-settings entrypoint and SettingsModal’s workspace navigation against real server binaries at desktop and phone widths; head opens and returns within the intended workspace, corroborated by four inspected screenshots.
  • add rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🦞 diamond lobster and patch quality is 🐚 platinum hermit.
  • add status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (terminal): Captured parent/head Chromium traces exercise ChatApp’s account-settings entrypoint and SettingsModal’s workspace navigation against real server binaries at desktop and phone widths; head opens and returns within the intended workspace, corroborated by four inspected screenshots.

Label justifications:

  • P2: This fixes confusing settings navigation for multi-workspace users and mobile drawer overlap with limited blast radius.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🦞 diamond lobster and patch quality is 🐚 platinum hermit.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (terminal): Captured parent/head Chromium traces exercise ChatApp’s account-settings entrypoint and SettingsModal’s workspace navigation against real server binaries at desktop and phone widths; head opens and returns within the intended workspace, corroborated by four inspected screenshots.
  • proof: sufficient: Contributor real behavior proof is sufficient. Captured parent/head Chromium traces exercise ChatApp’s account-settings entrypoint and SettingsModal’s workspace navigation against real server binaries at desktop and phone widths; head opens and returns within the intended workspace, corroborated by four inspected screenshots.

Evidence

What I checked:

  • Repository policy: Read the complete root AGENTS.md; its SQL-generation guidance does not apply to this frontend change. No nested AGENTS.md or maintainer-notes directory was found. (AGENTS.md:1, d753da3e1c60)
  • Change remains necessary on main: GitHub confirms the captured main revision. Its account modal still renders every workspace in listing order; the PR instead derives one selected workspace and retains the existing navigation handler and role-based visibility. (apps/web/src/components/settings/SettingsModal.svelte:63, 19e4c4e8631e)
  • Latest release retains previous navigation: The v0.5.0 modal also renders one group per workspace and has no currentWorkspaceID input, so the requested behavior is not already shipped. (apps/web/src/components/settings/SettingsModal.svelte, 648202b79b2f)
  • Focused implementation and regression coverage: The introduced source diff adds modal-local selection without changing stored preferences or settings routes. Seven helper tests and four browser tests cover current selection, explicit switching, a single workspace, and mobile drawer closure. Tests were inspected, not executed during this read-only review. (tests/e2e/settings-current-workspace.spec.ts:48, d753da3e1c60)
  • Captured real browser proof: The complete supplied PR body records Chromium runs against parent and head binaries at desktop and phone widths. On head, the selector shows TYP as current, Integrations opens under TYP’s route, and Close returns under that same route. All four prepared screenshots were inspected and corroborate the rail change. Captured context identity: 86e495f166b935a8dd3bfd0d3f06253cd89c1a0953a461e2258f9156f11fab76. (d753da3e1c60)
  • Proof driver inspection: The linked driver launches the actual server binary with a temporary data directory, creates workspaces through HTTP, and drives the account modal and settings routes in Chromium without mocked transport. It was read only and corroborates the captured transcript’s scenario. (repro-integrations.mjs)

Likely related people:

  • unknown: The claimed source-line change could not be verified from bounded local history. (role: source history unknown; confidence: low)
  • unknown: The claimed source-line change could not be verified from bounded local history. (role: source history unknown; confidence: low)

Rating scale

Score Internal tier Crab rank Meaning
6/6 S 🦀 challenger crab Exceptional readiness
5/6 A 🦞 diamond lobster Very strong readiness
4/6 B 🐚 platinum hermit Good normal PR; ordinary maintainer review
3/6 C 🦐 gold shrimp Useful, but confidence is limited
2/6 D 🦪 silver shellfish Proof or implementation needs work
1/6 F 🧂 unranked krab Not merge-ready
N/A NA 🌊 off-meta tidepool Rating does not apply

Overall follows the weaker of proof and patch quality.
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

Workflow

  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

P2 Normal priority bug or improvement with limited blast radius. proof: sufficient Contributor real behavior proof is sufficient. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant