# MVP v3.0 — 部署与验收流程（草案）

## 0) 环境前提
- Ray 集群：延续 v2.5 的 head + stateless worker（自动 join）
- 共享存储：容器内挂载 `/private`（dev/prod 对齐）
- API server：宿主机代码挂载到 head 容器，在 head 容器内启动
- 新增：SFTPGo 服务（建议容器化部署）

## 1) 部署步骤（高层）

1) 部署/升级 Ray 节点镜像（沿用 v2.5 的 `argus/argus-ray-node:v2.5` 或更高版本）
2) 启动 Ray 集群（compose 或平台创建容器）
3) 启动/配置 SFTPGo（挂载 `/private`）
4) 启动 API server（head 容器内）
5) 启动 WebUI（由 API server 托管）

## 2) 验收用例（必须通过）

### A. 用户与凭据
1) admin 创建用户 `alice`，签发 API token
2) 系统联动在 SFTPGo 创建 `alice`（home=/private/users/alice）
3) `alice` 使用 token 登录 WebUI（或调用 `/api/v2/me` 成功）

### B. 上传数据闭环（核心）
1) `alice` 通过 SFTP 上传数据集到 `/private/users/alice/datasets/...`
2) `alice` 通过 WebUI/API 提交任务，TaskSpec 引用该路径
3) Ray worker 读取该数据，任务 RUNNING 并最终 SUCCEEDED

### C. 下载产物闭环
1) 训练完成后，产物落到 `/private/users/alice/jobs/<submission_id>/...`
2) `alice` 通过 SFTP 下载 checkpoints/logs 成功
3) （新增）`alice` 将需要长期保留的权重从 `jobs/<submission_id>/...` 移动到 `models/`，确认移动后可长期存在

### C2. Jobs 回收站与自动清理（3 天移入回收站，7 天后永久删除）
1) 将 `jobs_trash_after_days`/`jobs_purge_after_days` 配置为较小值（例如分钟级，用于验证）
2) 训练完成进入 terminal 状态
3) 等待 API server 内置 janitor 扫描周期后，确认对应 `jobs/<submission_id>` 被移动到 `trash/jobs/<submission_id>`
4) 在回收站窗口内，把某个文件从 `trash/jobs/<submission_id>` 移动到 `models/`，确认移动成功
5) 等待超过 `jobs_purge_after_days` 后，确认 `trash/jobs/<submission_id>` 被永久删除
6) 确认已移动到 `models/` 的文件不被删除

### D. 安全隔离（必须）
1) `bob` 不能通过 API 查询 `alice` 的 task（404）
2) `bob` 不能提交引用 `/private/users/alice/...` 的 TaskSpec（400/403）
3) `bob` 通过 SFTP 无法访问 `/private/users/alice/...`（chroot 生效）

## 3) 故障注入（推荐通过）
1) kill worker watchdog 或 raylet → worker 自动恢复并重新加入集群
2) 重启 head 容器 → head 重新写 `head.json`，worker 自动重连
3) SFTPGo 重启 → 不影响 Ray 集群；用户可重新连接上传/下载

## 4) 回归清单（与 v2.5 一致）
- 任务队列、重试（INSUFFICIENT_RESOURCES → PENDING_RESOURCES → retry）
- PPO/GRPO/SFT 三种 workload 均可跑通
- head 不跑训练（driver 强制落 worker）
