# 组织成员移出与 Workspace 成员语义规范

> Canonical path: `specs/organization/member-removal.md`
> 状态：目标设计（克制版）
> 相关规范：
> - [AgentTeam 组织与权限规范](./organization.md)
> - [组织侧 Workspace 访问管理规范](./workspace-access-management.md)

本规范定义 organization 成员移出能力，以及 organization 成员列表与 workspace 成员列表的职责边界。

---

## 1. 背景

当前产品有两类“成员”入口：

- organization 侧成员页：展示 organization members，支持邀请、审批、组织角色更新。
- workspace 侧成员页：展示当前 workspace members，支持当前 workspace 内成员管理。

现状问题：

- organization 成员页没有“移出组织”能力；当前不是单纯 UI 缺按钮，API / store / frontend wrapper 也没有 org member remove 链路。
- 两个页面都叫“成员”，但一个管组织目录，一个管 workspace 协作参与，用户容易混淆。
- 如果只禁止 human 访问但不处理其 active agent / machine，移出组织后仍可能有可行动身份继续运行，存在安全缺口。

目标是补一个克制、可解释的最小闭环，不重构 org/workspace 双层模型。

---

## 2. 模型边界

### 2.1 Organization member

`organization_member` 表示“这个 user 是否属于该 organization”。它是目录、治理、计费和 workspace 授权的事实来源。

organization 成员页应表达：

- 邀请组织成员；
- 审批/撤销组织邀请；
- 修改组织角色；
- 移出组织。

### 2.2 Workspace member

workspace `member` 表示“这个 principal 在某个 workspace 内的协作身份”。它承载消息、任务、agent、machine、runtime 行为边界。

workspace 成员页应表达：

- 添加成员到当前 workspace；
- 修改当前 workspace role；
- 从当前 workspace 移除；
- 查看当前 workspace 内的 agent / machine 影响。

### 2.3 成员来源

workspace member 可以来自不同来源：

- direct：在当前 workspace 直接添加；
- org member grant：organization 直接授权某个人进入该 workspace；
- org team grant：organization 通过 team 授权一组人进入该 workspace；
- legacy/local：历史或非 org 模式下的 workspace-local 成员。

建议 workspace 成员列表显示“来源”，帮助用户判断应该在 organization 侧撤销 grant，还是在 workspace 侧移除成员。

---

## 3. 产品语义

### 3.1 “移出组织”

“移出组织”表示：

> 该 user 不再是 organization 的 active member，也不能再以该身份访问该 organization 下的 workspace 或继续运行代表他的 active agent / machine。

这不是 hard delete。

必须保留：

- 历史消息；
- 任务记录；
- 审计记录；
- agent 曾经发出的消息；
- member / agent / machine 的历史行，除非已有独立的数据保留策略要求清理。

必须停止未来行动：

- 该 user 对该 organization 的访问；
- 该 user 对该 organization 下 workspace 的有效访问；
- 该 user 拥有或绑定的 active agent / machine / runtime session。

### 3.2 “从当前 workspace 移除”

“从当前 workspace 移除”只影响一个 workspace：

- 不把 user 移出 organization；
- 不影响其它 workspace；
- 按当前 workspace offboarding 语义处理该 workspace 内的 agent / machine。

### 3.3 不合并两个列表

organization 成员列表和 workspace 成员列表应继续存在。调整重点是命名、文案和操作语义，而不是合并模型。

---

## 4. MVP Scope

### 4.1 In scope

1. organization 成员页新增“移出组织”。
2. 后端新增 organization member removal impact preview。
3. 后端新增 organization member soft removal。
4. 移出时停用目标 user 在受影响 workspace 内可继续行动的 active agent / machine。
5. UI 文案区分“组织成员”和“Workspace 成员”。
6. workspace 成员列表展示成员来源（如果已有数据能稳定表达；否则可作为后续增强）。

### 4.2 Non-goals

MVP 不做：

- hard delete user / organization_member / workspace member；
- 删除历史消息、任务、审计、附件；
- 合并 organization/workspace 成员列表；
- 重构完整 RBAC；
- 引入新的 policy DSL；
- 自动转移 workspace owner 或 agent owner；
- 处理跨 organization 的全局账号注销；
- 与外部目录/SSO 做双向删除同步。

---

## 5. UX 方案

### 5.1 Organization 成员页

页面标题/说明建议明确为“组织成员”。

成员行操作：

- 修改组织角色；
- 移出组织。

“移出组织”确认弹窗必须展示 impact preview：

- 目标成员身份；
- 将失去访问的 workspace 数量与列表；
- 将被停用/归档的 active agents；
- 将被撤销或停用的 machines / runtime sessions；
- 是否存在阻塞项，例如最后一个 org owner、workspace 唯一 owner。

确认文案必须明确：

- 历史消息、任务和审计记录会保留；
- 该成员不能再访问该 organization 及其 workspace；
- active agent / machine 会停止未来行动；
- 如需恢复，需要重新邀请或由管理员重新授权。

### 5.2 Workspace 成员页

页面标题/说明建议明确为“Workspace 成员”。

移除按钮文案应是“从当前 workspace 移除”。确认弹窗必须说明：

- 只影响当前 workspace；
- 不会把成员移出 organization；
- 会处理当前 workspace 内该成员相关 agent / machine 的 offboarding。

### 5.3 来源展示

workspace 成员列表建议显示来源：

| 来源 | 用户理解 | 主要撤销入口 |
|------|----------|--------------|
| 直接添加 | 在当前 workspace 里直接加入 | Workspace 成员页 |
| 组织授权 | organization 给个人授权到 workspace | Organization Workspaces / grant 管理 |
| 团队授权 | organization team 授权到 workspace | Organization Teams / Workspaces |
| 本地/历史 | 旧模型或非 org 模式 | Workspace 成员页 |

---

## 6. API 契约

### 6.1 Impact preview

```text
GET /api/v1/orgs/:orgId/members/:memberId/removal-impact
```

返回建议结构：

```json
{
  "member": { "id": "orgmem_...", "displayName": "...", "role": "org_member" },
  "blockingReasons": [
    { "code": "last_org_owner", "message": "..." },
    { "code": "sole_workspace_owner", "workspaceId": "...", "workspaceName": "..." }
  ],
  "affectedWorkspaces": [
    { "id": "wsp_...", "name": "...", "role": "member", "source": "team_grant" }
  ],
  "agentsToDisable": [
    { "id": "agt_...", "name": "...", "workspaceId": "wsp_..." }
  ],
  "machinesToRevoke": [
    { "id": "mch_...", "name": "...", "workspaceId": "wsp_..." }
  ]
}
```

### 6.2 Remove member

```text
DELETE /api/v1/orgs/:orgId/members/:memberId
```

语义：soft remove / deactivate，不 hard delete。

成功返回：

```json
{
  "memberId": "orgmem_...",
  "status": "inactive",
  "disabledAt": "..."
}
```

错误：

- `403`：actor 不是 org owner/admin；
- `409 self_remove_not_allowed`：不能移出自己；
- `409 last_org_owner`：不能移出最后一个 owner；
- `409 sole_workspace_owner`：目标是某 workspace 唯一 owner；
- `409 sync_managed_member`：若后续决定 sync-managed 成员不允许本地移除；
- `404`：member 不存在或不属于 org。

---

## 7. 后端执行语义

新增 service method 建议形态：

```text
PreviewOrganizationMemberRemovalImpact(ctx, actorUserID, orgID, orgMemberID)
RemoveOrganizationMemberForUser(ctx, actorUserID, orgID, orgMemberID)
```

### 7.1 事务内步骤

1. 校验 organization active。
2. 校验 actor 是 active org owner/admin。
3. 锁定目标 `organization_members` 行。
4. 校验目标 active 且属于该 org。
5. 阻止自移除。
6. 阻止最后一个 org owner。
7. 计算目标 user 在该 org 下所有 active workspace membership 与 owner 关系。
8. 阻止 workspace 唯一 owner。
9. 将目标 organization member 置 inactive：
   - `status = inactive`
   - `disabled_at = now()`
   - `disabled_by = actor`（如果现有 schema 没字段，可先只写已有字段，另列迁移）
10. 失效该成员的 org-level direct workspace grants。
11. 移除或失效该成员的 team/unit membership。
12. Reconcile 该成员在所有 org-owned workspace 的 effective access。
13. 停用/归档该 user 在受影响 workspace 中拥有的 active agents。
14. 撤销该 user 绑定的 active machines / runtime sessions。
15. 发布必要的 workspace/org 成员更新事件，刷新 UI/cache。

### 7.2 Workspace access 处理

移出组织后的稳定规则：

> active human 对 org-owned workspace 的有效访问必须依赖 active organization member。

实现可选择两种方式之一，但用户语义必须一致：

- 在 removal transaction 中把该 user 在 org-owned workspace 的 active human `members` 行置 inactive；
- 或在 authorization/projection 层统一要求 active organization member，并同步更新 projection 状态。

MVP 推荐第一种：显式 inactive，便于现有 workspace access check 和 UI 立即一致。

注意：这仍不是 hard delete。历史 row 保留。

### 7.3 Agent / machine 处理

移出组织不能让目标 user 的 active agent 继续行动。

MVP 规则：

- 只处理 human-owned / user-bound active agents；
- archive / disable，而不是 hard delete；
- 停止 runtime session 或让后续调度拒绝启动；
- 保留 agent 历史消息和审计；
- 若 agent owner 已先被转移给其它 active member，则不处理该 agent。

machine 规则：

- revoke 目标 user 绑定的 active machines；
- 断开或失效相关 runtime control token；
- 保留 machine 历史记录。

### 7.4 邀请与 grants

MVP 建议：

- revoke 目标 email/user 在该 org 下 pending org invites（如果可关联）；
- 已接受 invites 保留历史；
- active team/unit membership 置 inactive 或删除 desired membership；
- active direct workspace grants 置 revoked/inactive；
- team grant 本身不因成员移出而删除，只通过 membership 变化影响 projection。

---

## 8. 权限与保护规则

### 8.1 Actor

允许：

- `org_owner`
- `org_admin`

不允许：

- 普通 org member；
- 仅 workspace owner/admin，但不是 org owner/admin 的用户。

### 8.2 Target guardrails

必须阻止：

- 移出自己；
- 移出最后一个 active org owner；
- 导致任何 active workspace 没有 owner；
- 移出 already inactive member 时产生非幂等副作用。

可以允许：

- org owner 移出 org admin/member；
- org admin 移出普通 member；
- org admin 是否能移出另一个 org admin 需要产品确认，MVP 可先仅允许 owner 移出 admin。

---

## 9. 数据一致性

### 9.1 保留历史

所有 offboarding 操作都应使用 status / revoked / disabled 状态保留历史，而不是物理删除。

### 9.2 幂等

重复调用 remove：

- 如果目标已 inactive，应返回成功或稳定的 no-op；
- 不应重复 archive agent 或重复 revoke machine 导致错误。

### 9.3 Reconciliation

需要复用或扩展现有 organization workspace projection reconciliation，确保：

- org member inactive 后，其 direct grant 不再物化 active workspace access；
- team membership inactive 后，team grant 不再给该 user 物化 access；
- workspace `#all` / scope membership 与 workspace member 状态一致。

---

## 10. 测试计划

### 10.1 Store / service tests

覆盖：

- org owner/admin 可 preview/remove 普通 member；
- 普通 member 不可 remove；
- self remove 被拒绝；
- last org owner 被拒绝；
- sole workspace owner 被拒绝；
- target organization member 变 inactive；
- org direct grants/team memberships 失效；
- workspace members 变 inactive 或 effective access 消失；
- active agents/machines 被 disable/revoke；
- 历史消息/任务不被删除。

### 10.2 API tests

覆盖：

- impact route response shape；
- delete route success/error code；
- repeated delete 幂等；
- authorization boundary。

### 10.3 Frontend tests

覆盖：

- organization members page 显示移出组织；
- self/owner guard 显示 disabled 或后端错误；
- impact modal 展示 workspace/agent/machine 影响；
- workspace members page 文案为“从当前 workspace 移除”。

---

## 11. Rollout

建议顺序：

1. 后端 impact preview 只读接口。
2. 后端 remove service + API，先隐藏 UI。
3. 前端 organization members remove action + confirm modal。
4. workspace members 来源展示。
5. 打开 feature flag / internal rollout。

可观测指标：

- remove success/failure count；
- block reason distribution；
- disabled agents/machines count；
- post-removal access denied events；
- unexpected workspace owner block。

---

## 12. Open questions

1. `org_admin` 是否可以移出另一个 `org_admin`，还是只能 owner 做？
2. sync-managed / SSO 成员是否允许本地移出，还是只允许 suspend override？
3. 是否需要 `disabled_by`、`disabled_reason` 字段迁移？
4. 对 workspace-direct membership，MVP 是直接置 inactive，还是由 auth 层统一要求 active org member？
5. agent disable 后是否允许 owner transfer + restore？恢复入口在哪里？
6. machine revoke 是否需要异步任务处理离线 daemon？
7. 是否需要单独的 organization offboarding audit event 表？

---

## 13. Summary

克制方案是：

- 保留 organization/workspace 双层成员模型；
- 明确两个成员列表的用户语义；
- organization removal 做 soft deactivate；
- 不删历史内容；
- 停止未来访问和可行动 agent/machine；
- 先做 impact preview + confirm + 最小 API/store/UI 闭环；
- 不把它扩展成全量账号注销或权限系统重构。
