MotirBuilding in public
MOTIR · moooon
onMotir
You’re viewing a public project. Anyone can view it — no account needed. Sign in to submit, upvote, or comment on requests.View-only — you can’t edit work items
MOTIR-3502

(motir-core) Hide the workspace-settings SURFACE at one workspace — fold Name / Members / Danger into org settings and 404 the area

Done
Description

The surface arm of MOTIR-3500 — ship §6d's settings collapse, which the header half of progressive disclosure shipped without.

docs/decisions/organization-tier.md §6: "At one workspace the workspace-settings surface is hidden … the single Settings home folds in the workspace-config sections." ShellTierNav.tsx:103 gates the switcher at >= 2 and nothing else is gated, so at count 1 a surface titled "Workspace settings" — carrying Name, Members and a Delete workspace danger zone — is reachable from four entry points, one of which renders only in the collapsed state.


⚠️ RE-SCOPED TWICE. Read this before building.

2026-08-25 — the fold-in TARGET refuses a non-admin. /settings/organization returned the forbidden EmptyState to anyone who is not an org owner/admin (page.tsx:53-56), and a workspace invitee is a plain org member. So ACs 4+5 as first written left a teammate with no team roster and no route to Leave the workspace — its only surface (DangerZoneCard) renders on exactly the two routes the collapse closes to them.

2026-08-26 — MOTIR-3519 settled it: the settings home gates PER SECTION, not per page. Option A of the two candidates. ADR §6d and design/org-admin/design-notes.md now both carry the rule; this card's ACs 4, 5 and 9 are its build.

The general form the decision states, which this card must implement rather than special-case: relocating a surface preserves its GATE. A hidden tier changes what the product NAMES, never what a user may DO. Concretely — and note that rename and delete are membership-gated, not admin-gated (workspacesService.renameWorkspace / deleteWorkspace assert membership only), so hosting these for a plain member widens nothing:

section on /settings/organizationwho sees it
org-scoped (org name, billing, org danger zone)org owner/admin only — a plain org member gets panel 5d's forbidden treatment for those sections
the folded-in workspace sections (Name, Members, Danger zone)any member of that workspace
nothing at alla non-member of the org — 404, never 403

Already built — committed on parent/MOTIR-3500-progressive-disclosure-one-workspace, no pull request

Resume from that branch, not from origin/main. tsc clean, 42 assertions green. Settled and unaffected by the decision:

  • lib/workspaces/tierDisclosure.ts (+ its .server half) — the reveal predicate, computed ONCE for the shell, the entry points and the route's own gate, replacing the lone >= 2 literal in ShellTierNav. Split because two readers are 'use client'.
    • ⚠️ The predicate is the VIEWER's workspaces in the active org, not the ORG's — what has always shipped, and correct for a disclosure rule. This is the OPPOSITE of what MOTIR-3501's §5 arm reads. ADR §5/§6 now say so explicitly. Do not unify them.
  • The four entry points, two of them RE-POINTED rather than removed: the rail's no-project settings door and ⌘K's "Go to settings" target /settings/organization, since AC 1 asks that no markup name the workspace area and with no active project that rail row is the only settings door there is. Only the UserMenu row — literally labelled "Workspace settings" — is absent.
  • WorkspaceConfigCardWorkspaceFoldInSection: a MOUNT of the same three cards.
  • The revalidatePath fan-out — the cards render on two routes now.
  • The en/zh pair, two dead keys dropped.

Still to build: the per-section gate (AC 4), the notFound() (AC 5), and the E2E re-point (AC 9).

Acceptance criteria

  1. Rendering the shell for an org with ONE workspace produces no markup naming the /settings/workspace AREA in UserMenu, AppCommandPalette or SidebarNav's settings door — asserted grep-shaped on the exact href, so the workspace-SCOPED sub-routes are not caught by it. A re-pointed door satisfies this; the door need not vanish. ✅ built
  2. /settings/organization at ONE workspace renders the workspace Name, Members and Delete-workspace cards in its stack, and the old "Open workspace settings" button is gone. ✅ built
  3. Saving the workspace name from the folded-in card shows the new value on that same page without a manual reload (the revalidatePath fan-out). ✅ built
  4. [Set by MOTIR-3519] /settings/organization gates per section: a plain org member reaches the page and sees the folded-in workspace sections for a workspace they belong to, while the org-scoped cards show panel 5d's forbidden treatment. An org owner/admin sees both. A non-member of the org still gets 404, never 403.
  5. [Set by MOTIR-3519] At the collapsed count /settings/workspace answers 404 for every actor — asserted on a production build, the status being the assertion — and every capability it carried has a route: the folded-in danger zone renders Leave workspace for a plain member of that workspace, asserted by name.
  6. /settings/workspace/github, /settings/workspace/gitlab and /settings/workspace/jobs answer 200 at every workspace count and org role, unchanged.
  7. At TWO workspaces every entry point renders and /settings/workspace is unchanged from main.
  8. No new loading.tsx is added anywhere above app/(authed)/settings/workspace, and tests/navigation/loading-boundary-guard.test.ts stays green.
  9. Every consumer of the changed contracts is updated in the same PR. tests/components/UserMenu-platform-door.test.tsx and SidebarNav-settings-door.test.tsx are already repaired on the branch; tests/e2e/workspace-flows.spec.ts:159-205 is the one that matters — it reads the active workspace's name at /settings/workspace because the switcher is hidden (its own comment says so) and then has the invitee Leave there. Re-point it at the folded-in section; it is the end-to-end proof of AC 5's second half.

Context refs

  • docs/decisions/organization-tier.md §6 + §6d (the per-section rule, the Leave table, the whose count clause)
  • design/org-admin/design-notes.md — the amended Where it lives block; design/workspaces/settings.png
  • lib/workspaces/tierDisclosure.ts + .server.ts — the shared predicate
  • app/(authed)/settings/organization/page.tsx (:53-56, the whole-page gate AC 4 replaces), _components/WorkspaceFoldInSection.tsx
  • app/(authed)/_components/{UserMenu,AppCommandPalette,SidebarNav,ShellTierNav,TopNav}.tsx, layout.tsx
  • app/(authed)/settings/workspace/page.tsx, actions.ts, _components/{NameCard,MembersCard,DangerZoneCard}.tsx
  • lib/services/workspacesService.tsrenameWorkspace / deleteWorkspace assert membership, not admin
  • tests/e2e/workspace-flows.spec.ts:159-205, tests/e2e/org-admin.spec.ts:269,304,378, tests/e2e/shell-a11y.spec.ts:65
  • CLAUDE.md § A loading.tsx may NOT sit above a route that decides existence

Repo: every criterion is discharged in motir-core.

Sizing: re-estimated 5 → 8 pts / 85 → 120 min by MOTIR-3519, which added the per-section gate. Still under the 13-point split signal, and it is NOT split: the doors, the rooms and the host's gate are one state — landing them separately leaves an interval where the doors are hidden and the rooms are unreachable, which is strictly worse than today.