# 多注册流程侧边栏能力矩阵

本文解决的是一个很容易被低估的问题：sidepanel 现在不只是“渲染 UI”，它还承担了大量业务约束。如果未来要支持多个 flow，不能只做动态渲染，还必须把“哪些能力允许显示、允许切换、允许启动”做成统一能力矩阵。

## 1. 当前源码里的真实问题

当前手机号注册能力至少同时受这几组条件约束：

- `phoneVerificationEnabled`
- `plusModeEnabled`
- `contributionMode`
而这些约束现在分散在多个地方：

- `background.js`：`canUsePhoneSignup`
- `background/message-router.js`：同类判断
- `sidepanel/sidepanel.js`：`canSelectPhoneSignupMethod`
这说明 sidepanel 当前既在做展示，也在做策略判定，而且判定逻辑有重复。

## 2. 设计目标

后续 sidepanel 至少要拆成三层能力判断：

1. `flowCapabilities`
2. `panelCapabilities`
3. `runtimeLocks`

最终所有显示 / 禁用 / 启动校验都只读这三层组合结果。

## 3. 三层能力定义

### 3.1 `flowCapabilities`

表示“当前 flow 业务上支持什么”。

示例：

```js
flowCapabilities.openai = {
  supportsEmailSignup: true,
  supportsPhoneSignup: true,
  supportsPhoneVerificationSettings: true,
  supportsPlusMode: true,
  supportsContributionMode: true,
  supportsPlatformBinding: ['cpa', 'sub2api', 'codex2api'],
  supportsLuckmail: true,
  supportsOauthTimeoutBudget: true,
  stepDefinitionMode: 'openai-dynamic',
};

flowCapabilities.siteA = {
  supportsEmailSignup: true,
  supportsPhoneSignup: false,
  supportsPhoneVerificationSettings: false,
  supportsPlusMode: false,
  supportsContributionMode: false,
  supportsPlatformBinding: ['siteA-panel'],
  supportsLuckmail: false,
  supportsOauthTimeoutBudget: false,
  stepDefinitionMode: 'siteA',
};
```

原则：

- 没有真实需求，不要默认给新 flow 打开手机号、Plus、LuckMail
- 新 flow 默认从最小能力集合开始

### 3.2 `panelCapabilities`

表示“当前面板来源允许什么交互”。

示例：

```js
panelCapabilities = {
  cpa: {
    supportsPhoneSignup: true,
  },
  sub2api: {
    supportsPhoneSignup: true,
  },
  codex2api: {
    supportsPhoneSignup: true,
  },
};
```

注意：`contributionMode` 不是新的 `panelMode`，它是独立运行态锁。

### 3.3 `runtimeLocks`

表示“当前这一次运行或当前 UI 状态是否允许操作”。

示例：

```js
runtimeLocks = {
  autoRunLocked: false,
  contributionMode: false,
  plusModeEnabled: false,
  phoneVerificationEnabled: true,
  settingsMenuLocked: false,
};
```

它决定的是：

- 现在能不能切换注册方式
- 能不能改 flow
- 能不能导入导出配置
- 能不能打开某些配置区

## 4. 统一选择器

建议提供一个中心 selector：

```js
resolveSidepanelCapabilities({
  activeFlowId,
  panelMode,
  state,
});
```

输出：

```js
{
  effectiveSignupMethods: ['email', 'phone'],
  canShowPhoneSettings: true,
  canSelectPhoneSignup: true,
  canShowPlusSettings: true,
  canShowLuckmail: true,
  canExportSettings: true,
  canSwitchFlow: false,
  stepDefinitionOptions: {},
}
```

以后 sidepanel 与 background 都只消费这份结果，不再各写一套判断。

## 5. OpenAI 当前矩阵

### 5.1 仅看 flow 能力

OpenAI 当前业务上支持：

- 邮箱注册
- 手机号注册
- OAuth 登录
- 平台回调绑定
- Plus
- LuckMail
- 接码配置

### 5.2 加上 runtime lock 后的真实结果

OpenAI 的手机号注册只有在以下条件同时成立时才可选：

- `flowCapabilities.openai.supportsPhoneSignup === true`
- `phoneVerificationEnabled === true`
- `plusModeEnabled === false`
- `contributionMode === false`

也就是说，手机号注册不是一个“单纯 UI 开关”，而是能力矩阵结果。

### 5.3 加上 panel 约束后的真实结果

当前 `panelMode` 仍会影响来源能力与步骤定义，但不再额外弹出手机号注册风险提醒。

## 6. 步骤列表也属于能力矩阵的一部分

当前步骤定义主要受：

- `plusModeEnabled`
- `signupMethod`

影响，并通过 `data/step-definitions.js` 动态切换。

多 flow 后必须升级成：

```js
getStepDefinitions({
  activeFlowId,
  signupVariant,
  panelMode,
  plusModeEnabled,
  contributionMode,
});
```

也就是说，步骤列表不再是全局 `10 步 / 13 步` 二选一，而是 flow-aware 的定义。

## 7. Sidepanel 分层职责

### 7.1 展示层

只负责：

- show / hide
- enabled / disabled
- 文案与提示

### 7.2 能力判定层

只负责：

- `resolveSidepanelCapabilities()`
- `validateAutoRunStart()`
- `validateModeSwitch()`

### 7.3 后台复核层

后台在收到启动、切换注册方式、切换 flow 等消息时，仍要复核一遍同一套能力判断，避免 sidepanel 以外的入口绕过约束。

## 8. 新 flow 的默认能力策略

后续新增 flow 时，默认按以下最小集合开始：

- 仅邮箱注册
- 无手机号注册
- 无 Plus
- 无 LuckMail
- 无贡献模式
- 无 OpenAI 平台回调面板

只有真实需求出现时，再逐项打开 capability。

## 9. 迁移顺序

1. 提炼 `flowCapabilitiesRegistry`
2. 提炼 `panelCapabilitiesRegistry`
3. 新增 `resolveSidepanelCapabilities()` 与 `validateAutoRunStart()`
4. 让 `sidepanel.js`、`background.js`、`message-router.js` 共用同一套 selector
5. 再把步骤定义切换也改成 flow-aware

## 10. 本文对应解决的缺口

本文主要补齐以下缺口：

- sidepanel 不只是渲染层，还承接业务约束
- 手机号注册能力由多个开关共同决定，当前逻辑分散且重复
- `contributionMode` 与 `panelMode` 容易混淆
- 多 flow 之后，步骤列表也必须纳入能力矩阵，而不是继续全局写死
