# ADR 0020: Organization Is The SaaS Tenant Root

## Status

Accepted.

## Context

The Hub is moving from a single-organization deployment assumption toward a
SaaS shape. The existing permission model already supports team principals via
`PermissionGrant(PROJECT, TEAM, role)`, but without a tenant root the data model
cannot safely answer:

- which customer owns a project;
- which organization a team belongs to;
- whether a team grant crosses customer boundaries;
- which external directory sync produced a Feishu department/user-group
  membership;
- where future org admin, billing, audit, and platform break-glass controls
  attach.

Retrofitting this after projects, teams, runs, and audit records grow would
force tenant assumptions into every caller.

## Decision

Introduce `Organization` as the SaaS tenant root.

Customer-side collaboration data is organization-scoped:

```text
Organization
  OrganizationMembership
  Team
  TeamMembership
  Project
  ExternalDirectoryConnection
  PermissionGrant
```

`User` remains global. A user can belong to multiple organizations through
`OrganizationMembership`. Project-level authorization still uses
`PermissionGrant` and `PermissionAuthorizer`; the authorizer derives the
organization from the requested resource, then resolves teams and external
principals in that organization scope.

`Team` belongs to exactly one organization. `Project` belongs to exactly one
organization. A `PermissionGrant` that grants a `TEAM` principal on a `PROJECT`
resource is valid only when the team and project are in the same organization.
This invariant is pinned in `Spec.System.Organization`.

External directory membership is scoped through `ExternalDirectoryConnection`:

```text
ExternalDirectoryConnection(
  organizationId,
  provider,
  providerTenantId,
  source
)
```

`ExternalPrincipalMembership` points at a connection rather than carrying a
free-floating `source` string. `source` names a sync pipeline; it is not the
tenant identity.

The SaaS platform control plane is separate from customer project permissions.
`READ/EDIT/MANAGE` remains the customer-side project permission lattice.
Platform staff access, tenant suspension, billing, and break-glass access are a
separate control plane and must not be modeled as project `MANAGE`.

## Consequences

- Existing single-org data is backfilled into a default organization during
  migration.
- `Team.slug` becomes organization-scoped instead of globally unique.
- Actor resolution is organization-aware: direct teams and synchronized external
  principals only expand inside the resource's organization.
- Direct `USER` grants remain possible because a global user can collaborate
  across organizations when explicitly granted.
- Future org admin UI can be built on this model without changing the
  authorization core.

## Open Questions / Deferred

- Full platform admin UI, billing, and tenant suspension workflows are
  deferred. Production safety limits and workload brakes are no longer
  deferred; ADR-0022 defines them without treating a workload brake as tenant
  suspension.
- Whether direct `USER` project grants should require active
  `OrganizationMembership` is deferred; explicit direct grants remain allowed
  for now.
- Provider-specific directory semantics beyond Feishu are deferred, but the
  connection model leaves room for them.
