# 运行时架构重构改动清单（Runtime Refactor Plan）

**日期：** 2026-02-24  
**作者：** AI Assistant  
**状态：** Ready for Execution

## 审批状态流转（Release Readiness）

| 状态 | 进入条件 | 退出条件 |
|---|---|---|
| Draft | 计划初稿创建完成 | 评审材料齐备并发起评审 |
| In Review | 架构/平台/后端评审已指派 Owner | 阻断项全部关闭 |
| Ready for Execution | 阻断项关闭，且满足本节“生效条件” | 任一生效条件失效，回退到 In Review |
| In Execution | 按 Phase 开始实施并产生变更记录 | 全部 Phase 完成并通过验收 |
| Completed | 验收、回滚演练、文档归档全部完成 | 无 |

### 当前状态
- `Ready for Execution`

### `Ready for Execution` 生效条件（必须同时满足）
- 根 `AGENTS.md` 的 Compose 路径规范已与唯一标准对齐（`infra/docker/docker-compose.yml`），且校验命令通过。
- `runtime-bootstrap-gate` 已写入技术防绕过实施项（CI 依赖、统一执行器、自动检测）。
- 发布门禁、回滚演练、路由验收硬门槛已在计划内形成闭环，且命令可直接执行。

## 1. 标题与目的

### 1.1 重构目标
- 将长驻进程启动方式从 `infra/scripts/*.py` 包装脚本，迁移为编排层直接执行可观测命令（`uvicorn/gunicorn/celery`）。
- 将迁移/初始化从“混合入口脚本”改为 one-shot job（独立 CLI 命令 + Compose 一次性服务）。
- 配置策略从 `SOCIAL_RUNTIME__ENVIRONMENT` 分支控制，迁移为显式参数控制（web/worker 各自配置键）。
- Celery 从父进程拉多队列（`worker.py` 内部 `Popen`）改为按任务类型分组的独立 worker 服务。

### 1.2 非目标
- 不改动业务接口、领域逻辑、数据库 schema、Auth 流程。
- 不新增消息中间件类型（仍使用 Redis 作为 Celery broker/backend）。
- 不引入新的部署平台（仅调整当前 Docker Compose 运行路径）。

### 1.3 约束
- 仅调整运行时与运维启动路径，保持 API 行为兼容。
- 保持 `backend/src/core/config/settings.py` 作为唯一环境变量入口。
- 不在后端运行时代码中新增 `os.environ` 直读。
- 迁移期间允许“双轨短期兼容”，legacy 脚本至少保留一个发布周期（定义为一个正式版本迭代窗口）。
- legacy 脚本删除作为独立里程碑执行，不与入口切换同批发布。
 
### 1.4 验收标准
- 发布门禁：`init-job bootstrap` 成功（exit code=0）是 `web/worker` 启动前置条件，未通过禁止发布。
- 发布门禁唯一入口：本地与 CI 统一执行 `runtime-bootstrap-gate`，禁止任何绕过 gate 的 `up web/worker-*`。
- Compose 可直接拉起 web 与分组 worker（至少 `critical/default/bulk` 三组），且均通过健康/存活检查。
- `SOCIAL_RUNTIME__ENVIRONMENT` 不再决定 web 入口类型（gunicorn/uvicorn 由显式开关控制）。
- 新旧配置并存期遵循“新键优先、旧键告警、版本截止”规则（见第 6 节）。

#### 1.4.1 可测门槛（必须全部满足）

执行上下文约束：所有 Python 命令统一在仓库根目录执行，并显式携带 `PYTHONPATH=backend/src`；禁止切换为 `cd backend` 写法。

| 类别 | 必跑命令/检查 | 通过门槛 |
|---|---|---|
| 发布门禁 | `docker compose --env-file .env -f infra/docker/docker-compose.yml run --rm init-job bootstrap` | exit code = 0 |
| 启动验证（强制经 gate） | `make runtime-bootstrap-gate && docker compose --env-file .env -f infra/docker/docker-compose.yml ps` | 两条命令 exit code = 0，且 `ps` 中 `web/worker-critical/worker-default/worker-bulk/redis/db` 均为 Up |
| 健康检查 | `curl -fsS http://127.0.0.1:8000/health` | exit code = 0 |
| Unit 测试 | `PYTHONPATH=backend/src uv run pytest backend/tests/unit/test_process_settings.py backend/tests/unit/core/runtime/test_cli.py -q` | exit code = 0 |
| Integration 测试 | `PYTHONPATH=backend/src uv run pytest backend/tests/integration -q` | exit code = 0 |
| E2E 最小链路 | `PYTHONPATH=backend/src uv run pytest backend/tests/e2e/test_infra_health_e2e.py -q` | exit code = 0 |
| 覆盖率门槛 | `PYTHONPATH=backend/src uv run pytest backend/tests/unit backend/tests/integration --cov=backend/src --cov-report=term-missing --cov-fail-under=80` | 覆盖率 >= 80%，exit code = 0 |

### 1.4.2 Gate 与命令使用边界（消除绕过歧义）

| 命令类型 | 允许命令 | 使用场景 | 约束 |
|---|---|---|---|
| 发布/上线（唯一入口） | `make runtime-bootstrap-gate`（内部固定执行 `init-job bootstrap` -> `up web/worker-*`） | 本地发布、CI、手工发布、验收 | 必须使用；禁止在 gate 之外直接执行 `up web/worker-*` |
| 演练/排障（非发布） | `docker compose ... up -d web worker-critical worker-default worker-bulk redis db`、`docker compose ... logs ...`、`docker compose ... ps` | 故障复现、回滚演练、压测、本地调试 | 必须在 runbook 标注为“演练/排障命令”；不得作为发布放行依据 |

判定规则：任何“发布成功”结论都必须包含 `runtime-bootstrap-gate` 成功记录；仅执行直接 `up web/worker-*` 的记录一律判定为不合规。

### 1.4.3 路由验收硬门槛（必须满足）

| 类别 | 必跑命令/检查 | 通过门槛 |
|---|---|---|
| 路由用例（critical） | `PYTHONPATH=backend/src uv run pytest backend/tests/integration/test_celery_routing.py::test_route_critical_task_to_critical_queue -q` | exit code = 0，断言命中 `critical` 队列 |
| 路由用例（default） | `PYTHONPATH=backend/src uv run pytest backend/tests/integration/test_celery_routing.py::test_route_default_task_to_default_queue -q` | exit code = 0，断言命中 `default` 队列 |
| 路由用例（unknown -> default + 告警） | `PYTHONPATH=backend/src uv run pytest backend/tests/integration/test_celery_routing.py::test_route_unknown_task_to_default_with_warning -q` | exit code = 0，断言未知任务落到 `default` 且产生日志告警 |
| 路由验收判定 | `PYTHONPATH=backend/src uv run pytest backend/tests/integration/test_celery_routing.py::test_route_critical_task_to_critical_queue backend/tests/integration/test_celery_routing.py::test_route_default_task_to_default_queue backend/tests/integration/test_celery_routing.py::test_route_unknown_task_to_default_with_warning -q` | 三条用例均通过；`critical/default` 命中正确；unknown 路由不失败且有告警 |

## 2. 影响面清单（按文件）

说明：以下为本次重构的**目标改动清单**，按 Create / Modify / Delete 分类。

### 2.1 Create
| 文件路径 | 用途 |
|---|---|
| `backend/src/core/runtime/cli.py` | one-shot 运行命令入口（migrate/init-data） |
| `backend/tests/unit/core/runtime/test_cli.py` | CLI 参数与流程单测 |
| `docs/runtime/runtime-runbook.md` | 新运行方式（web/worker/job）操作手册 |

### 2.2 Modify
| 文件路径 | 变更说明 |
|---|---|
| `infra/docker/docker-compose.yml` | 新增/改造 `web`、`worker-critical`、`worker-default`、`worker-bulk`、`init-job` 服务及依赖 |
| `backend/src/core/config/settings.py` | 新增显式 web/worker 配置模型，弱化 `runtime.environment` 分支语义 |
| `backend/src/core/celery/app.py` | 增加队列路由配置与默认队列策略，适配分组 worker |
| `backend/AGENTS.md` | 在 Phase 0 提前更新运行入口说明（从脚本切换到编排层直启 + one-shot job） |
| `.env.example` | 新增显式启动参数，保留兼容期映射说明 |
| `backend/tests/unit/test_process_settings.py` | 更新设置项断言（显式参数优先） |

### 2.3 Deprecate（Phase 0-4）
| 文件路径 | 处理方式 |
|---|---|
| `infra/scripts/web.py` | 标记 deprecate，保留一个发布周期，不做物理删除 |
| `infra/scripts/worker.py` | 标记 deprecate，保留一个发布周期，不做物理删除 |
| `infra/scripts/bootstrap.py` | 标记 deprecate，保留一个发布周期，不做物理删除 |
| `backend/tests/unit/infra/test_web_script.py` | 标记 deprecate，保留到 Phase 5 |
| `backend/tests/unit/infra/test_worker_script.py` | 标记 deprecate，保留到 Phase 5 |

### 2.4 Delete（仅 Phase 5）
| 文件路径 | 删除原因 |
|---|---|
| `infra/scripts/web.py` | deprecate 窗口结束后物理删除 |
| `infra/scripts/worker.py` | deprecate 窗口结束后物理删除 |
| `infra/scripts/bootstrap.py` | deprecate 窗口结束后物理删除 |
| `backend/tests/unit/infra/test_web_script.py` | 随 legacy 脚本物理删除一并移除 |
| `backend/tests/unit/infra/test_worker_script.py` | 随 legacy 脚本物理删除一并移除 |

## 3. 分阶段改造计划

## Phase -1：规范对齐阻断项（必须先完成，0.25 天）

### 变更项（强制前置）
- 修订根 `AGENTS.md` 的 Docker Compose 示例路径：从 `docker/docker-compose.yml` 统一为 `infra/docker/docker-compose.yml`。
- 明确“单一规范”：仓库所有文档/脚本/CI 示例仅允许 `docker compose --env-file .env -f infra/docker/docker-compose.yml ...`。
- 将该项设置为 Phase 0 之前的硬门禁；未完成不得进入 runtime 重构实施。

### 验收检查命令（强制）
```bash
rg -n "docker compose .* -f docker/docker-compose.yml" AGENTS.md docs/ infra/ backend/
rg -n "docker compose .* -f infra/docker/docker-compose.yml" AGENTS.md
docker compose --env-file .env -f infra/docker/docker-compose.yml config
```

### 通过判定
- 第一条命令无输出（无旧路径残留）。
- 第二条命令命中根 `AGENTS.md`。
- 第三条命令 exit code = 0。

## Phase 0：基线冻结与兼容窗口（0.5 天）

### 变更项
- 在 `docs/runtime/runtime-runbook.md` 记录当前启动命令基线与回滚命令。
- 前移更新 `backend/AGENTS.md`：将“脚本入口”改为“编排层直启 + one-shot job”。
- 统一 Compose 文件路径：仅允许 `infra/docker/docker-compose.yml`，修订范围包括 `AGENTS.md`、runbook、`.env.example`、CI 脚本/文档示例。
- 约定兼容窗口：先引入新入口并灰度验证，再删除脚本入口。

### 风险点
- 团队并行开发期间继续调用旧入口，导致“看似可用、实际分叉”。

### 回滚策略
- 仅文档与约定变更，回滚为恢复旧 runbook 即可。

### 验证命令
```bash
docker compose --env-file .env -f infra/docker/docker-compose.yml config
PYTHONPATH=backend/src uv run pytest backend/tests/unit/test_process_settings.py -q
```

### 验收方式
- 文档闭环（量化）：`rg -n "docker compose .* -f docker/docker-compose.yml" AGENTS.md docs/ infra/ backend/` 无输出，且 `rg -n "docker compose .* --env-file .env -f infra/docker/docker-compose.yml" AGENTS.md backend/AGENTS.md docs/runtime/runtime-runbook.md .env.example` 命中 >= 4。
- 规范闭环（量化）：`rg -n "runtime-bootstrap-gate|init-job bootstrap.*前置|禁止.*up web/worker" backend/AGENTS.md docs/runtime/runtime-runbook.md` 命中 >= 2，且命中内容覆盖“前置 + 禁绕过”两类语义。

## Phase 1：Web 进程直连编排层（1 天）

### 变更项
- `infra/docker/docker-compose.yml` 新增/调整 `web` 服务：
  - `command` 直接执行 `uv run uvicorn app:app ...`（dev）或 `uv run gunicorn app:app ...`（prod-like）。
  - `working_dir` 指向 `backend/`，`PYTHONPATH=/app/backend/src`（按容器挂载实际路径）。
- `backend/src/core/config/settings.py` 新增显式键（示例）：
  - `SOCIAL_WEB__SERVER=uvicorn|gunicorn`
  - `SOCIAL_WEB__HOST`、`SOCIAL_WEB__PORT`
  - `SOCIAL_WEB__RELOAD`
  - `SOCIAL_WEB__GUNICORN__WORKERS` 等
- 去除“仅 prod 走 gunicorn”的硬编码分支依赖。

### 风险点
- `working_dir/PYTHONPATH` 不一致导致 `app` 模块加载失败。
- gunicorn 与 uvicorn 参数映射不一致，造成性能/稳定性偏差。

### 回滚策略
- 保留 `web-legacy` profile（临时）可切回 `uv run python infra/scripts/web.py`。
- 回滚 `settings.py` 新字段，恢复 `app+gunicorn+runtime.environment` 逻辑。

### Legacy 回滚资产（硬前置）
- 落地 `legacy` profile 资产：`bootstrap-legacy`、`web-legacy`、`worker-legacy` 三个服务必须可启动且命令固定。
- 在 `docs/runtime/runtime-runbook.md` 固化 legacy 回滚步骤、期望结果、故障排查项。
- 产出并归档一次演练记录（时间、执行人、命令、日志路径、结果、问题单链接）。

### Phase 1 DoD（新增硬门槛）
- `web` 新入口通过第 1.4.1 的发布门禁与健康检查。
- legacy profile 资产已落地并可执行：`docker compose --env-file .env -f infra/docker/docker-compose.yml --profile legacy config` 返回 0。
- **必须完成一次第 14 节回滚演练并留档；未留档不得进入 Phase 2。**

### 验证命令（第 2-4 条仅用于演练/排障，非发布放行）
```bash
docker compose --env-file .env -f infra/docker/docker-compose.yml run --rm init-job bootstrap
docker compose --env-file .env -f infra/docker/docker-compose.yml up -d web redis db
docker compose --env-file .env -f infra/docker/docker-compose.yml logs web --tail=200
curl -fsS http://127.0.0.1:8000/health
```

### 发布门禁
- 若 `init-job bootstrap` 非 0 退出码，禁止启动 `web` 并阻断发布流程。

## Phase 2：迁移/初始化 one-shot job 化（1 天）

### 变更项
- 创建 `backend/src/core/runtime/cli.py`，提供子命令：
  - `migrate`（执行 alembic upgrade head）
  - `init-data`（调用 `core.initialization.init_data.initialize_data`）
  - `bootstrap`（按顺序执行 migrate + init-data）
- Compose 新增 `init-job`（`docker compose run --rm init-job bootstrap`）。
- 设定强制门禁：仅允许在 `bootstrap` 成功后启动 `web/worker`。
- `infra/scripts/bootstrap.py` 标记 deprecate 并移出主路径（删除延后至 Phase 5）。

### 风险点
- CLI 中同步/异步调用混用导致退出码不准确。
- 迁移 job 与 web 同时启动出现竞态。

### 回滚策略
- 保留 `bootstrap-legacy` profile（短期）可回退旧脚本。
- 若 job 失败，恢复旧 `bootstrap.py` 并维持“初始化先行”的启动前置条件。

### 验证命令
```bash
docker compose --env-file .env -f infra/docker/docker-compose.yml run --rm init-job migrate
docker compose --env-file .env -f infra/docker/docker-compose.yml run --rm init-job init-data
docker compose --env-file .env -f infra/docker/docker-compose.yml run --rm init-job bootstrap
```

## Phase 3：Celery 队列分组与独立服务（1.5 天）

### 变更项
- `infra/docker/docker-compose.yml` 拆分 worker 服务：
  - `worker-critical` -> `--queues=critical`
  - `worker-default` -> `--queues=default`
  - `worker-bulk` -> `--queues=bulk`
- `backend/src/core/config/settings.py` 增加队列分组显式配置：
  - `SOCIAL_WORKER__GROUPS__CRITICAL__CONCURRENCY` 等
  - 每组支持 `pool/time_limit/prefetch_multiplier/max_tasks_per_child`
- `backend/src/core/celery/app.py`：
  - `task_default_queue` 固定显式默认值（建议 `default`）
  - 增加 `task_routes`（按任务名前缀或模块路由）
  - 统一异常策略：未知任务路由默认回落 `default`，并输出结构化告警（`event=celery.route.fallback`）
  - 保持 `task_create_missing_queues=False`（避免拼写错误静默自动建队列）
- `infra/scripts/worker.py` 标记 deprecate（父进程多子 worker 模式退役，删除延后至 Phase 5）。

### 风险点
- 无任务路由策略时任务可能全部落到默认队列，造成优先级失效。
- 队列切分后并发参数不当，导致 CPU 抖动或长尾任务阻塞。
- 缺少统一指标阈值会导致“已拆分但不可观测”。

### 回滚策略
- 暂保留 `worker-legacy` profile，以单 worker 多队列方式兜底。
- 发现路由异常时，临时将 `task_routes` 回退为全量默认队列。

### 验证命令（仅演练/排障，发布必须走 `make runtime-bootstrap-gate`）
```bash
docker compose --env-file .env -f infra/docker/docker-compose.yml up -d worker-critical worker-default worker-bulk redis
docker compose --env-file .env -f infra/docker/docker-compose.yml ps
docker compose --env-file .env -f infra/docker/docker-compose.yml logs worker-critical --tail=120
docker compose --env-file .env -f infra/docker/docker-compose.yml logs worker-default --tail=120
docker compose --env-file .env -f infra/docker/docker-compose.yml logs worker-bulk --tail=120
```

### 压测与回归要求
- 必须先完成第 15 节并发基线压测，再冻结 `SOCIAL_WORKER__GROUPS__*` 默认值。

## Phase 4：收口与发布门禁固化（0.5 天）

### 变更项
- 固化不可绕过门禁：`init-job bootstrap` 作为 CI 必经 job（`runtime-bootstrap-gate`），`deploy` job 必须 `needs: [runtime-bootstrap-gate]`，gate 失败即终止流水线并禁止后续 `web/worker` 启动与发布步骤。
- 发布脚本与手工发布流程必须先显式执行 `docker compose --env-file .env -f infra/docker/docker-compose.yml run --rm init-job bootstrap`，不得依赖 one-shot `depends_on` 语义来隐式放行。
- 新增本地统一执行器：`infra/scripts/runtime-bootstrap-gate.sh` 与 `make runtime-bootstrap-gate`（仅封装执行顺序，不改变主入口策略）。
- `docs/runtime/runtime-runbook.md` 新增“绕过风险”章节：直接执行 `up web/worker-*` 的风险、排障影响、审计追踪缺失。
- 新增 CI 自动检测绕过检查：若在 gate 之外检测到直接 `up web/worker-*`，流水线失败。
- 更新 runbook、`.env.example` 与 CI 指南，确保新入口一致。
- 补充容器化 smoke 流程到 CI，覆盖 `worker-critical/default/bulk` 三组。

### `runtime-bootstrap-gate` 唯一执行入口（本地/CI共用）

```bash
bash -euo pipefail -c '
docker compose --env-file .env -f infra/docker/docker-compose.yml run --rm init-job bootstrap
docker compose --env-file .env -f infra/docker/docker-compose.yml up -d web worker-critical worker-default worker-bulk redis db
'
```

- 以上模板是唯一允许的串行入口：第一条命令非 0 时 shell 立即退出，第二条 `up web/worker-*` 不得执行。
- 本地手工执行、发布脚本、CI stage 均必须复用同一模板（stage 名称固定 `runtime-bootstrap-gate`）。

### 技术防绕过校验命令（强制）
```bash
rg -n "runtime-bootstrap-gate" .github/workflows
rg -n "needs:\s*\[?runtime-bootstrap-gate\]?" .github/workflows
rg -n "docker compose .*up -d .*\b(web|worker-critical|worker-default|worker-bulk)\b" .github/workflows
rg -n "runtime-bootstrap-gate" Makefile infra/scripts docs/runtime/runtime-runbook.md
```

判定规则：
- `deploy` 相关 job 必须显式依赖 `runtime-bootstrap-gate`。
- 直接 `up web/worker-*` 仅允许出现在 gate 执行器（脚本或 Make target）中；若出现在其他 CI job，判定为绕过并阻断。

### 风险点
- 文档未同步导致新成员仍按旧命令启动。

### 回滚策略
- 若发布窗口紧，维持 legacy 脚本 deprecate 状态并延后删除。

### 验证命令（第 4-6 条仅演练/排障；发布放行看 gate 记录）
```bash
PYTHONPATH=backend/src uv run pytest backend/tests/unit -q
PYTHONPATH=backend/src uv run pytest backend/tests/integration -q
PYTHONPATH=backend/src uv run pytest backend/tests/e2e/test_infra_health_e2e.py -q
docker compose --env-file .env -f infra/docker/docker-compose.yml run --rm init-job bootstrap
docker compose --env-file .env -f infra/docker/docker-compose.yml up -d web worker-critical worker-default worker-bulk redis db
curl -fsS http://127.0.0.1:8000/health
```

## Phase 5：legacy 删除里程碑（独立发布周期后，0.5 天）

### 触发条件
- 新入口已稳定运行至少一个发布周期。
- 第 14 节回滚演练最近一次结果为通过。

### 变更项
- 删除 `infra/scripts/web.py`、`infra/scripts/worker.py`、`infra/scripts/bootstrap.py`。
- 删除 `backend/tests/unit/infra/test_web_script.py`、`backend/tests/unit/infra/test_worker_script.py`。
- 清理文档中所有 legacy 命令。

### 回滚策略
- 若删除后发现阻塞，可从发布标签恢复 legacy 脚本并回滚到上一版本镜像。

### 验证命令
```bash
PYTHONPATH=backend/src uv run pytest backend/tests/unit backend/tests/integration -q
docker compose --env-file .env -f infra/docker/docker-compose.yml config
```

## 4. 详细改动清单（Checklist，按优先级）

- [ ] **P0-1：新增 runtime CLI one-shot 命令入口**  
  DoD：`PYTHONPATH=backend/src uv run python -m core.runtime.cli migrate|init-data|bootstrap` 均返回正确 exit code；`init-data` 调用 `initialize_data()`。  
  预计影响：替换 `bootstrap.py`，降低混合脚本复杂度。

- [ ] **P0-0：根 AGENTS Compose 路径规范修订（强制前置）**  
  DoD：根 `AGENTS.md` 已统一为 `infra/docker/docker-compose.yml`；全仓无 `docker/docker-compose.yml` 启动示例残留；`docker compose ... config` 校验通过。  
  预计影响：消除规范冲突，避免运行入口歧义。

- [ ] **P0-2：Compose 新增 `init-job` 并接入 one-shot 流程**  
  DoD：`docker compose run --rm init-job bootstrap` 可重复执行（幂等）且 exit code=0；该步骤失败时禁止启动 `web/worker`。  
  预计影响：迁移/初始化路径标准化，便于 CI/CD 执行。

- [ ] **P0-3：Compose 直启 web，移除对 `web.py` 依赖**  
  DoD：`web` 容器命令不再包含 `infra/scripts/web.py`；且启动前已通过 `init-job bootstrap`；`/health` 可用。  
  预计影响：减少一层 Python 子进程包装，排障路径更直接。

- [ ] **P0-4：前移更新 `backend/AGENTS.md` 与 Compose 路径统一闭环**  
  DoD：`backend/AGENTS.md` 已改为新入口规范；仓库文档/脚本示例统一为 `infra/docker/docker-compose.yml`。  
  预计影响：降低路径歧义与误启动概率。

- [ ] **P1-1：显式 web 配置键落地（`SOCIAL_WEB__*`）**  
  DoD：`SOCIAL_RUNTIME__ENVIRONMENT` 修改不再影响 web server 选型；选型由 `SOCIAL_WEB__SERVER` 控制。  
  预计影响：配置可预测性提升，环境分支耦合降低。

- [ ] **P1-2：worker 拆分为 `critical/default/bulk` 独立服务**  
  DoD：Compose 中存在 3 个 worker 服务，均只消费各自队列且日志区分明确。  
  预计影响：任务隔离增强，避免高耗时任务拖慢关键任务。

- [ ] **P1-3：Celery 路由显式化并禁用自动建队列**  
  DoD：`task_routes` 生效，未知任务默认回落 `default` 且输出 `celery.route.fallback` 告警，`task_create_missing_queues=False` 仍生效。  
  预计影响：线上路由错误可提前暴露，运行风险可控。

- [ ] **P2-1：删除 legacy 脚本与脚本单测**  
  DoD：该项仅在 Phase 5（独立里程碑）执行；满足“保留一个发布周期 + 回滚演练通过”后才可移除。  
  预计影响：技术债下降，入口唯一化。

- [ ] **P2-2：更新文档与 env 模板**  
  DoD：`backend/AGENTS.md`、`.env.example`、`docs/runtime/runtime-runbook.md` 三处命令一致。  
  预计影响：降低 onboarding 成本与误操作概率。

## 5. 队列拆分策略

### 5.1 推荐分组
- `critical`：用户同步感知任务（验证码发送、鉴权后置关键动作），目标低延迟。
- `default`：常规异步任务（中等耗时，可容忍轻微排队）。
- `bulk`：批处理/重计算/可延迟任务（高耗时、吞吐优先）。

### 5.2 何时需要“一队列一服务”
- 单任务类型 P95 执行时长显著高于其他任务（>3x）。
- 任务资源模型明显不同（CPU 密集 vs IO 密集）。
- 任务失败重试会产生级联影响，需要隔离故障域。
- 业务优先级强约束（SLA 严格，不能被普通任务占满并发）。

### 5.3 建议默认并发（起步值）
- `critical`: `concurrency=2~4`, `prefetch_multiplier=1`
- `default`: `concurrency=2`
- `bulk`: `concurrency=1~2`, `max_tasks_per_child` 较小以抑制内存膨胀

## 6. 配置迁移表（旧 -> 新）

| 旧配置 | 新配置 | 迁移说明 |
|---|---|---|
| `SOCIAL_RUNTIME__ENVIRONMENT=prod` + `SOCIAL_GUNICORN__ENABLED_IN_PROD=true` | `SOCIAL_WEB__SERVER=gunicorn` | server 选型不再依赖 environment 分支 |
| `SOCIAL_APP__HOST` | `SOCIAL_WEB__HOST` | web 显式命名，避免与业务 app 配置混淆 |
| `SOCIAL_APP__PORT` | `SOCIAL_WEB__PORT` | 同上 |
| `SOCIAL_APP__RELOAD` | `SOCIAL_WEB__RELOAD` | reload 仅用于开发态 web 进程 |
| `SOCIAL_GUNICORN__WORKERS` | `SOCIAL_WEB__GUNICORN__WORKERS` | 参数归并到 web 维度 |
| `SOCIAL_WORKER__ENABLED_QUEUES=[...]` | Compose 服务拆分（`worker-critical/default/bulk`）+ `SOCIAL_WORKER__GROUPS__*` | 去掉父进程多队列拉起模式 |
| `infra/scripts/bootstrap.py --migrate/--init-data` | `PYTHONPATH=backend/src uv run python -m core.runtime.cli migrate/init-data/bootstrap` | one-shot job 统一命令入口 |
| `SOCIAL_RUNTIME__SERVICE_NAME`（脚本注入） | Compose `service` 名称 + logging `service_name` 显式传参 | 不再依赖脚本注入环境变量 |

### 6.1 新旧配置并存规则（强制）
- 优先级：新配置键（`SOCIAL_WEB__*`、`SOCIAL_WORKER__GROUPS__*`）优先于旧键；若同时存在，以新键为准。
- 告警：检测到旧键生效或新旧同设时，启动日志输出 `DEPRECATION WARNING`（包含键名、替代键、截止版本）。
- 兼容截止版本：旧键兼容保留至 `v0.9`，自 `v1.0` 起移除旧键解析。
- 冲突处理：若新旧键值冲突，以新键执行并记录一次结构化告警事件（含 service_name 与环境）。

## 7. 测试策略（最小可执行集）

### 7.1 Unit
- 保留并扩展：`backend/tests/unit/test_process_settings.py`（新配置解析与默认值）。
- 新增：`backend/tests/unit/core/runtime/test_cli.py`（CLI 子命令、错误码、调用顺序）。
- legacy 保留期：`backend/tests/unit/infra/test_web_script.py`、`backend/tests/unit/infra/test_worker_script.py` 标记 deprecate，随 Phase 5 删除。

### 7.2 Integration
- 新增/调整：验证 Celery 路由配置可将任务落入预期队列（至少 `default` 与 `critical`）。
- 校验 one-shot `bootstrap` 流程在测试 DB 可重复执行且无副作用异常。

### 7.3 E2E
- 保底保留：`backend/tests/e2e/test_infra_health_e2e.py`。
- 增加最小链路：`init-job -> web health -> worker process alive`。

### 7.4 容器化 smoke（必须）
说明：此处 `up` 命令仅用于 smoke 与排障，不可替代 `runtime-bootstrap-gate` 发布门禁。
```bash
docker compose --env-file .env -f infra/docker/docker-compose.yml run --rm init-job bootstrap
docker compose --env-file .env -f infra/docker/docker-compose.yml up -d web worker-critical worker-default worker-bulk redis db
curl -fsS http://127.0.0.1:8000/health
docker compose --env-file .env -f infra/docker/docker-compose.yml ps
```

## 8. 交付物清单

### 8.1 文档
- `docs/runtime/runtime-runbook.md`（新增）
- `backend/AGENTS.md`（更新运行入口说明）
- `.env.example`（新增显式配置键与迁移注释）

### 8.2 配置
- `infra/docker/docker-compose.yml`（新增 web/worker 分组/init-job 服务）
- `backend/src/core/config/settings.py`（新增 web/worker 显式配置结构）
- `backend/src/core/celery/app.py`（显式路由与默认队列）

### 8.3 脚本/代码
- `backend/src/core/runtime/cli.py`（新增）
- `infra/scripts/web.py`、`infra/scripts/worker.py`、`infra/scripts/bootstrap.py` 先标记 deprecate，Phase 5 删除

### 8.4 测试
- 新增 `backend/tests/unit/core/runtime/test_cli.py`
- `backend/tests/unit/infra/test_web_script.py` 随 Phase 5 删除
- `backend/tests/unit/infra/test_worker_script.py` 随 Phase 5 删除
- 更新 `backend/tests/unit/test_process_settings.py`

## 9. 主要风险与缓解

| 风险 | 影响 | 概率 | 缓解 |
|---|---|---|---|
| 入口切换后容器启动失败（路径/模块解析） | 高 | 中 | Phase 1 保留 legacy profile；先 `compose config` 再灰度起服务 |
| 队列路由配置缺失或漂移导致关键任务降级 | 高 | 中 | 统一 unknown->default+告警策略；增加 `test_route_unknown_task_to_default_with_warning` 并接入告警阈值 |
| 迁移 job 与 web 启动竞态 | 中 | 中 | CI 必经 `runtime-bootstrap-gate` 串行执行，`bootstrap` 成功后才允许 `up web/worker-*` |
| 文档与真实命令不一致 | 中 | 高 | Phase 4 强制三处文档一致性检查（AGENTS/runbook/.env.example） |

## 10. 预计工作量

| 阶段 | 预计耗时 |
|---|---|
| Phase -1 | 0.25 天 |
| Phase 0 | 0.5 天 |
| Phase 1 | 1 天 |
| Phase 2 | 1 天 |
| Phase 3 | 1.5 天 |
| Phase 4 | 0.5 天 |
| Phase 5 | 0.5 天（独立里程碑） |
| **总计** | **5.25 天（含独立里程碑）** |

## 11. 已决策项（含验收条件 / Owner / 截止版本）

| 决策项 | 已决策内容 | 验收条件 | Owner | 截止版本 |
|---|---|---|---|---|
| 运行门禁固化 | `init-job bootstrap` 为 CI 必经 job（`runtime-bootstrap-gate`），失败即终止 | CI 日志可见 gate job；当 `bootstrap` 非 0 时流水线终止且不执行 `up web/worker-*` | Platform/Infra | `v0.9` |
| Celery 路由最小策略 | 采用任务前缀路由（`tasks.critical.*` -> `critical`，其余默认 `default`）并保留 `bulk` 显式路由位；未知任务统一回落 `default` + 结构化告警 | integration 用例验证 `critical/default/unknown->default`；告警可检索 | Backend | `v0.9` |
| Compose 路径统一 | 全仓命令统一 `--env-file .env -f infra/docker/docker-compose.yml` | AGENTS、runbook、`.env.example`、CI 命令均一致且无旧路径残留 | Platform/Infra | `v0.9` |
| Gate 防绕过机制 | CI `deploy` 必须依赖 `runtime-bootstrap-gate`；本地/CI 统一执行器；绕过自动检测阻断 | 依赖关系可检索、绕过检查命令通过、runbook 已标注绕过风险 | Platform/Infra | `v0.9` |

## 12. 标准 CLI 调用规范（本地/容器/CI）

### 12.1 统一约束
- `docker compose` 固定使用：`--env-file .env -f infra/docker/docker-compose.yml`。
- Python 命令统一在仓库根目录执行，不使用 `cd backend`。
- `PYTHONPATH` 统一为：本地与 CI 使用 `backend/src`；容器内使用 `/app/backend/src`。

### 12.2 本地开发（host）
```bash
PYTHONPATH=backend/src uv run python -m core.runtime.cli bootstrap
PYTHONPATH=backend/src uv run uvicorn app:app --app-dir backend/src --host 0.0.0.0 --port 8000 --reload
PYTHONPATH=backend/src uv run celery -A core.celery.app worker --workdir backend/src --queues=default --loglevel=INFO
```

### 12.2.1 本地统一门禁（必须）
```bash
bash -euo pipefail -c '
docker compose --env-file .env -f infra/docker/docker-compose.yml run --rm init-job bootstrap
docker compose --env-file .env -f infra/docker/docker-compose.yml up -d web worker-critical worker-default worker-bulk redis db
'
```

判定规则：`bootstrap` 非 0 立即终止，且不得执行任何 `up web/worker-*`。

### 12.3 容器运行（compose）
说明：以下用于本地联调/排障；发布流程仍以 `runtime-bootstrap-gate` 作为唯一放行依据。
```bash
docker compose --env-file .env -f infra/docker/docker-compose.yml run --rm init-job bootstrap
docker compose --env-file .env -f infra/docker/docker-compose.yml up -d web worker-critical worker-default worker-bulk redis db
docker compose --env-file .env -f infra/docker/docker-compose.yml ps
```

### 12.4 CI 流程（pipeline）
```bash
bash -euo pipefail -c '
docker compose --env-file .env -f infra/docker/docker-compose.yml run --rm init-job bootstrap
docker compose --env-file .env -f infra/docker/docker-compose.yml up -d web worker-critical worker-default worker-bulk redis db
'
PYTHONPATH=backend/src uv run pytest backend/tests/unit/core/runtime/test_cli.py backend/tests/unit/test_process_settings.py -q
PYTHONPATH=backend/src uv run pytest backend/tests/integration -q
PYTHONPATH=backend/src uv run pytest backend/tests/e2e/test_infra_health_e2e.py -q
```

CI 要求：上述 gate step 名称固定为 `runtime-bootstrap-gate`，不得拆分为两个可独立重试的 step。

## 13. 队列拆分后的观测与告警基线

| 指标 | 目标阈值 | 告警条件 | 处置建议 |
|---|---|---|---|
| `queue_depth{queue=critical}` | P95 < 20 | 连续 5 分钟 >= 50 | 提升 critical 并发或排查阻塞任务 |
| `task_latency_seconds{queue=critical}` | P95 < 3s | 连续 10 分钟 P95 >= 8s | 检查路由漂移与上游突发 |
| `task_latency_seconds{queue=default}` | P95 < 30s | 连续 10 分钟 P95 >= 90s | 扩容 default 或拆分热点任务 |
| `task_failure_rate{queue=*}` | < 2% | 5 分钟窗口 >= 5% | 触发失败任务抽样与回滚评估 |
| `worker_process_alive{group=*}` | 全部存活 | 任一 group 存活实例 < 1 持续 2 分钟 | 自动重启并标记发布风险 |
| `worker_cpu_usage{group=*}` | < 80% | 连续 10 分钟 >= 90% | 下调并发或扩容容器配额 |

说明：至少落地前 5 个指标；第 6 个为推荐增强项。

## 14. 回滚演练用例（新入口 -> legacy）

### 14.0 前置资产与留档要求（硬前置）
- 资产清单：Compose `--profile legacy` 下必须存在并可执行 `bootstrap-legacy`、`web-legacy`、`worker-legacy`。
- 留档位置：`docs/runtime/rollback-drills/`，文件命名 `YYYY-MM-DD-runtime-legacy-drill.md`。
- 留档最小字段：版本号、镜像 digest、执行命令、开始/结束时间、关键日志路径、通过判定、遗留问题。
- 关卡规则：**Phase 1 完成后必须执行一次并留档；未留档不得进入 Phase 2/3/4/5。**

### 14.1 演练触发场景
- `init-job bootstrap` 成功但 `web/worker` 启动后出现持续错误率超阈值。
- 队列延迟超过第 13 节阈值且 30 分钟内无法恢复。

### 14.2 演练步骤
1. 记录当前版本号、镜像 digest、配置快照。
2. 停止新入口服务：`docker compose --env-file .env -f infra/docker/docker-compose.yml stop web worker-critical worker-default worker-bulk`。
3. 启动 legacy 入口（deprecate profile）：`docker compose --env-file .env -f infra/docker/docker-compose.yml --profile legacy run --rm bootstrap-legacy && docker compose --env-file .env -f infra/docker/docker-compose.yml --profile legacy up -d web-legacy worker-legacy redis db`。
4. 执行健康检查与关键任务回归（必须执行以下命令）：

```bash
# 健康探测（5 连续成功）
for i in 1 2 3 4 5; do
  curl -fsS http://127.0.0.1:8000/health >/dev/null || exit 1
done

# 关键路由回归（critical/default/unknown fallback）
PYTHONPATH=backend/src uv run pytest \
  backend/tests/integration/test_celery_routing.py::test_route_critical_task_to_critical_queue \
  backend/tests/integration/test_celery_routing.py::test_route_default_task_to_default_queue \
  backend/tests/integration/test_celery_routing.py::test_route_unknown_task_to_default_with_warning -q

# 基础链路回归
PYTHONPATH=backend/src uv run pytest backend/tests/e2e/test_infra_health_e2e.py -q

# legacy worker 告警与关键链路日志检索
docker compose --env-file .env -f infra/docker/docker-compose.yml --profile legacy logs worker-legacy --since=10m | rg "critical|celery.route.fallback"
```

5. 对比回滚前后 30 分钟指标（错误率、队列深度、延迟）。

### 14.3 通过判定
- `web-legacy` 与 `worker-legacy` 在 10 分钟内恢复服务。
- `/health` 连续 5 次探测通过（上方循环命令 exit code = 0）。
- 关键回归命令全部通过：三条 routing 集成测试 + `test_infra_health_e2e.py` 均 exit code = 0。
- `critical` 任务链路恢复，失败率回落至 < 2%，且 `worker-legacy` 日志 10 分钟窗口内至少出现 1 条 `critical` 命中记录。
- 演练记录文档已提交到 `docs/runtime/rollback-drills/` 且可被 CI/评审引用。

## 15. Worker 并发基线压测与调参回归

### 15.1 基线压测
- 压测对象：`worker-critical/default/bulk` 三组并发参数（`concurrency/prefetch_multiplier/max_tasks_per_child`）。
- 压测数据：至少包含短任务（<1s）、中任务（1~10s）、长任务（>10s）三类。
- 产出：每组生成吞吐、P95 时延、失败率、CPU/内存曲线基线报告。

### 15.2 调参流程
1. 固定流量模型，单次仅调整一个参数。
2. 每轮调参后运行 30 分钟压测并记录指标。
3. 若任一关键指标恶化超过 10%，回退上一组参数。
4. 选择满足 SLA 且资源利用率最优的一组作为默认值。

### 15.3 回归要求
- 每次 worker 参数变更必须复跑第 1.4.1 的全部可测门槛。
- 必跑回归：`test_cli.py`、`test_process_settings.py`、`test_infra_health_e2e.py`。
- 覆盖率不得低于 80%。

## 16. 文档级 Go 判定（可执行）

| 判定项 | 必跑命令 | Go 门槛 |
|---|---|---|
| 门禁一致性（禁止绕过） | `rg -n "runtime-bootstrap-gate|needs:\s*\[?runtime-bootstrap-gate\]?" .github/workflows && rg -n "docker compose .*up -d .*\b(web|worker-critical|worker-default|worker-bulk)\b" .github/workflows` | 第一条命中 >= 2；第二条仅允许命中 gate 执行器文件；否则 No-Go |
| 文档闭环 | `rg -n "docker compose .* -f docker/docker-compose.yml" AGENTS.md docs/ infra/ backend/ ; rg -n "docker compose .* --env-file .env -f infra/docker/docker-compose.yml" AGENTS.md backend/AGENTS.md docs/runtime/runtime-runbook.md .env.example` | 前者无输出；后者命中 >= 4 |
| 路由策略一致性 | `PYTHONPATH=backend/src uv run pytest backend/tests/integration/test_celery_routing.py::test_route_critical_task_to_critical_queue backend/tests/integration/test_celery_routing.py::test_route_default_task_to_default_queue backend/tests/integration/test_celery_routing.py::test_route_unknown_task_to_default_with_warning -q` | 3/3 用例通过，且 unknown 路由回落 default 并有告警 |
| 关键任务链路恢复（回滚场景） | `for i in 1 2 3 4 5; do curl -fsS http://127.0.0.1:8000/health >/dev/null || exit 1; done && PYTHONPATH=backend/src uv run pytest backend/tests/e2e/test_infra_health_e2e.py -q` | 健康探测 5/5 成功且 E2E 通过 |
| 质量门槛 | `PYTHONPATH=backend/src uv run pytest backend/tests/unit backend/tests/integration --cov=backend/src --cov-report=term-missing --cov-fail-under=80` | 覆盖率 >= 80%，exit code = 0 |

执行规则：以上 5 项全部满足才可判定文档级 Go；任一项不满足即回退 `In Review`。
