Skip to content

fix(platform-objects): hide org/membership surfaces in single-org mode - #2348

Merged
os-zhuang merged 2 commits into
mainfrom
fix/single-org-gate-membership-surfaces
Jun 26, 2026
Merged

os-zhuang merged 2 commits into
mainfrom
fix/single-org-gate-membership-surfaces

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Problem

The platform gates multi-org features two ways — nav entries on requiresService: 'org-scoping' (e.g. setup-nav Organizations/Invitations) and object actions on visible: 'features.multiOrgEnabled != false' (e.g. sys_organization.create_organization). That convention had only been wired in a handful of spots, so a wide band of org/membership surface leaked into single-org deployments (OS_MULTI_ORG_ENABLED unset/false) where it is pure UX noise or a broken affordance.

Triggering example: …/_console/apps/com.objectstack.account/sys_member/view/mine ("My Organizations") shows even in single-org, where there are no sys_organization rows and no auto-stamped memberships — so the view is always empty for every user.

Changes (metadata-only)

Nav (P1)

  • Account app nav_account_memberships (sys_member/mine): was gated on requiresObject: 'sys_member', which never fires (system object is always registered). Re-gated on requiresService: 'org-scoping'.
  • setup-nav nav_teams (sys_team): had no gate while sibling Organizations/Invitations were service-gated. Added requiresService: 'org-scoping'.

Actions — gated on features.multiOrgEnabled != false (P0)

  • sys_user.invite_user (most exposed — the Users list is always reachable in single-org)
  • sys_member: add_member / update_member_role / remove_member, and transfer_ownership (combined with its existing record.role != 'owner' condition)
  • sys_team: create_team / update_team / remove_team
  • sys_team_member: add_team_member / remove_team_member
  • sys_invitation: invite_user / resend_invitation / cancel_invitation (recipient-side accept/reject stay record-gated — unreachable in single-org anyway)

Remaining rough edges (P2)

  • sys_organization admin actions (update / delete / set_active / leave / change_slug) now all gated too — previously only create_organization was.
  • titleFormat no longer renders a null organization: sys_member → {user_id} ({role}), sys_invitation → Invitation for {email} (org_id is null in single-org → old formats read "… null").

sys_team's by_org view left as-is: the teams list is empty in single-org (create is gated) so the grouping never degenerates in practice, and the nav entry is hidden anyway.

Scope / safety

No behavior change in multi-org deployments (OS_MULTI_ORG_ENABLED=true): features.multiOrgEnabled is true and the org-scoping service is present, so every gate evaluates to visible exactly as before. No schema, API, or runtime changes.

Not in scope (follow-up): single-sourcing the multi-org flag resolution (driver-sql reads the env var directly instead of the resolved flag; ~8 scattered detection sites) and a regression gate asserting org-scoped surfaces are hidden in single-org.

Verification

  • @objectstack/platform-objects build + DTS succeed
  • Package tests 63/63 pass

🤖 Generated with Claude Code

os-zhuang and others added 2 commits June 26, 2026 21:39
Apply the platform's existing multi-org gating convention consistently
across the org/membership surface, which had only been wired in a handful
of spots:
- nav entries -> requiresService: 'org-scoping'
- object actions -> visible: 'features.multiOrgEnabled != false'

Nav (P1):
- account.app "My Organizations" (sys_member/mine): was gated on
  requiresObject: 'sys_member', which never fires (system object always
  registered) and left the always-empty view visible in single-org.
  Re-gated on requiresService: 'org-scoping'.
- setup-nav "Teams" (sys_team): had no gate while sibling Organizations/
  Invitations were service-gated. Added requiresService: 'org-scoping'.

Actions (P0) — gated on features.multiOrgEnabled != false:
- sys_user.invite_user (most exposed: Users list always reachable)
- sys_member add_member/update_member_role/remove_member + transfer_ownership
- sys_team create_team/update_team/remove_team
- sys_team_member add_team_member/remove_team_member
- sys_invitation invite_user/resend_invitation/cancel_invitation

Recipient-side invitation accept/reject stay record-gated (unreachable in
single-org anyway). No behavior change in multi-org. Metadata-only.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Gate the remaining sys_organization admin actions (update/delete/
  set_active/leave/change_slug) on features.multiOrgEnabled != false, so
  every org action matches the already-gated create_organization.
- Stop rendering a null organization in titleFormat:
  - sys_member: '{user_id} in {organization_id}' -> '{user_id} ({role})'
  - sys_invitation: 'Invitation to {organization_id}' -> 'Invitation for {email}'
  organization_id is null in single-org mode, so the old formats read
  "... null"; the new fields identify the record in both modes.

sys_team's by_org view was left as-is: the teams list is empty in single-org
(create is gated) so the grouping never degenerates in practice, and the nav
entry is hidden anyway.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 26, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
spec Ready Ready Preview, Comment Jun 26, 2026 1:55pm

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation tooling size/m labels Jun 26, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/platform-objects.

3 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/concepts/packages.mdx (via @objectstack/platform-objects)
  • content/docs/concepts/setup-app.mdx (via @objectstack/platform-objects)
  • content/docs/guides/packages.mdx (via @objectstack/platform-objects)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

@os-zhuang
os-zhuang merged commit f44c1bd into main Jun 26, 2026
16 checks passed
@os-zhuang
os-zhuang deleted the fix/single-org-gate-membership-surfaces branch June 26, 2026 14:02

This branch was successfully deployed

1 active deployment
Preview — 46749dd3 Deployed Jun 26, 2026 by vercel[bot]
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 tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant