# MVP v1.1 计划（Hardening + 多 Workload 可行性验证）

本目录是 `specs/mvp/v1/` 的下一步迭代：在 v1 已经跑通（Ray head + 2 worker，PPO on Ray，持久化落盘）的基础上，把它升级为**可长期回归**的最小系统，并扩展至少一个新 workload 的可行性闭环。

> v1.1 的目标不是做平台服务化（API/队列/多用户）——那是 v2/v3 的工作；v1.1 聚焦“工程化 + 可行性边界验证 + 可观测/可排障基础”。

---

## 1. v1 基线回顾（已完成）

- 拓扑：1 head（无 GPU，CPU/GPU=0）+ 2 worker（各 4 GPU）
- 提交方式：必须用 head 上的 `ray job submit`
- driver 调度：通过 `worker_node` 自定义资源 + `--entrypoint-resources` 强制 driver 在 worker
- 输出：按 `submission_id` 组织到共享目录（NFS）

相关实现参考：

- 脚本：`src/mvp/v1/`
- 验收动作：`specs/mvp/v1/v1_action.md`
- Roadmap：`specs/mvp/mvp_roadmap.md`

---

## 2. v1.1 目标（必须达成）

### 2.1 工程化（Hardening）

1) **JobSpec 标准化（最小）**
   - 把“提交 job 需要的参数”收敛成结构化文件：
     - Ray 基础配置（YAML）：cluster 地址、entrypoint 资源约束、runtime_env 等
     - 训练 JobSpec（YAML）：workload 语义与训练参数
   - 至少覆盖：`submission_id`、workload 类型、资源需求、共享根路径、模型/数据路径、输出目录、超时、环境变量注入。
   - v1.1 实现落点（已在 repo 里提供，SDK 方式）：
     - RayConfig 示例：`src/mvp/v1.1/py/configs/dev.yaml`
     - JobSpec 示例：`src/mvp/v1.1/py/jobspecs/{ppo,grpo,sft}.yaml`
     - 提交入口：`src/mvp/v1.1/py/run.py`（在 head 容器内执行，使用 Ray Python SDK 提交）
     - 设计文档：`specs/mvp/v1.1/sdk_submit_refactor.md`

2) **共享根路径抽象（dev/prod 一致）**
   - 引入 `SHARED_ROOT` 作为唯一共享根路径：
     - dev：建议也用 `/private`（docker compose 把宿主机 shared 挂到容器内 `/private`，模拟生产）
     - prod：固定 `/private`（算力平台容器内 NFS）
   - 任何代码/脚本不得写死 `/mnt/shared`（允许兼容旧路径但不得作为主路径）。

3) **共享目录分层（新增 `common/` 与 `user/`）**
   - 在 `datasets/hf/jobs/outputs` 之外，新增一个所有用户可读写的共享区：
     - `${SHARED_ROOT}/common/`：共享模型/数据/代码快照（多版本 verl / 公共数据）
     - `${SHARED_ROOT}/user/`：用户自定义代码（例如 `reward_fn_path` 指向这里）
   - v1.1 默认策略：先假设“所有用户可写”（后续 v3 再做权限与隔离）。

4) **可排障基础**
   - 每个 job 目录必须有：
     - `config/`：提交命令、JobSpec 快照、关键 env_vars
     - `logs/`：Ray job logs + hydra logs（如有）
     - `checkpoints/`：按 `save_freq` 控制频率（默认每 10 step）
    - 提供“失败快照”能力：收集 `ray status` / `ray job list` / `ray list nodes` / `ray list actors`（最少其中 2 项）写入 job 目录。
   - v1.1 submitter 默认落盘：
     - `${SHARED_ROOT}/jobs/<id>/config/job_spec.json`
     - `${SHARED_ROOT}/jobs/<id>/config/runtime_env.json`
     - `${SHARED_ROOT}/jobs/<id>/config/submit_cmd.txt`
     - `${SHARED_ROOT}/jobs/<id>/logs/ray_job_submit.out`
     - `${SHARED_ROOT}/jobs/<id>/debug/ray_status_{pre,post}.txt`
     - `${SHARED_ROOT}/jobs/<id>/debug/ray_job_list_post.txt`

### 2.2 Workload 扩展（至少新增 1 个）

v1.1 需要新增并验收通过两个 workload（都要跑通闭环）：

- **GRPO on Ray**（推荐优先，复用 PPO 入口，通过算法配置切换）
  - 基于 `python -m verl.trainer.main_ppo`
  - 通过配置覆盖：`algorithm.adv_estimator=grpo`（以及必要的 rollout 参数）

 - **SFT on Ray（Ray-native）**
   - 入口：`python -m verl.trainer.sft_trainer_ray`
   - 参考实现：`verl/verl/trainer/sft_trainer_ray.py`（内部会 `ray.init()`）
   - 需要确保 `ray.init()` 连接已有集群：
     - 优先：`runtime_env.env_vars.RAY_ADDRESS=auto`（配合 `ray job submit`）
     - 兜底：在 v1.1 的 launcher 脚本里显式 `ray.init(address="auto")` 再调用 trainer（避免依赖 Ray 的 env var 行为差异）
   - 重要细节：Ray Job 的 entrypoint（driver）默认不分配 GPU，因此 SFT driver 侧不要强依赖 CUDA：
     - 推荐：`trainer.device=cpu`（driver 只做 orchestration；训练由 Ray workers 占 GPU）

---

## 3. v1.1 关键设计点

### 3.1 多版本代码与自定义逻辑（为 v3.1 铺路，但 v1.1 先做最小验证）

已确定优先方案（A）：通过 **Ray Job 的 `runtime_env.env_vars`** 注入 `PYTHONPATH`。

- `code_path`（例如 `${SHARED_ROOT}/common/code/verl/<commit>`）
- 提交 job 时设置：
  - `runtime_env.env_vars.PYTHONPATH = "<code_path>:$PYTHONPATH"`

并约定：

- `reward_fn_path` 可指向 `${SHARED_ROOT}/user/code/...` 下用户自定义代码
- 与 `code_path` 一样，必须通过 `runtime_env.env_vars` 确保该路径可被 import（例如把 `${SHARED_ROOT}/user/code` 也加入 `PYTHONPATH`）

v1.1 中至少做一次“代码覆盖验证”：

- 在 code_path 下放一个可识别的 `verl` 版本标识（例如 `verl.__version__` 打印差异）
- 提交 job 并在日志中确认 import 的是 code_path 的版本（而不是镜像内默认安装）

v1.1 的最小落地方式（已实现）：

- 提供代码快照脚本：`src/mvp/v1.1/scripts/31_snapshot_verl_code.sh`
  - 会把 `/workspace/verl`（挂载的 repo）复制到 `${SHARED_ROOT}/common/code/verl/<code_id>/`
  - 并写入 `${code_path}/mvp_marker.py`，用于在 Ray job logs 中验证“选用的是哪份 code_path”
- submitter 会在 entrypoint 前运行 preflight：
  - 打印 `verl.__file__` 与 `mvp_marker.MARKER`
  - 由此确认 job 粒度的 PYTHONPATH 生效，且不同 job 可指向不同 `code_path`（多版本共存）

### 3.2 Checkpoint 策略（磁盘保护）

- 默认：`save_freq=10`（每 10 step 保存一次）
- 对于 step 数已知的短任务（例如 29 steps），可以通过配置把 `save_freq` 调整为 10/15/29（按需求权衡）
- 作业目录按 `submission_id` 隔离，方便清理与归档

---

## 4. v1.1 交付物清单（代码 + 文档）

### 4.1 代码（建议落点）

在 `src/mvp/` 下新增 v1.1 级别的提交器与模板（或在 `src/mvp/v1` 原地演进但要保持 v1 可回归）：

- `src/mvp/v1.1/`
  - `docker-compose.yaml`（与 v1 互不干扰的容器名/网络名）
  - `scripts/`（Ray 启动/prepare 保留 bash；submit 通过 SDK 工具执行）
  - `py/`（工程化提交层：YAML + Ray Python SDK）
    - `py/configs/`（Ray 基础配置）
    - `py/jobspecs/`（训练 JobSpec）
    - `py/run.py`（入口）

此外，为了对齐 dev 环境约束（远程机固定目录）：

- 远程机目录必须新增：`argus@h1:/home2/argus/infra/mvp/v1.1/`
- 该目录内需包含 v1.1 的全部内容（compose + scripts + README），可由本 repo 的 `src/mvp/v1.1/` 同步过去

### 4.2 文档

- `specs/mvp/v1.1/v1.1_action.md`：开发、部署、测试、验收流程（可复现）
- 更新 `specs/mvp/mvp_roadmap.md`：保持路线图与落地一致（按需）

---

## 5. v1.1 验收标准（DoD）

### 5.1 Hardening DoD

- [ ] 所有提交均由 head 执行 `ray job submit`，且显式 `--submission-id=<id>`
- [ ] 共享根路径由 `SHARED_ROOT` 控制（dev/prod 可切换），脚本无硬编码
- [ ] 每个 job 的输出目录为：`${SHARED_ROOT}/jobs/<submission_id>/`
- [ ] checkpoint 不会“每 step 保存”导致爆盘：默认 `save_freq=10`
- [ ] job 失败时，`${SHARED_ROOT}/jobs/<id>/config/` 中有足够信息定位（命令、env、ray 状态快照）
 - [ ] v1.1 测试前会清理 v1 的遗留容器/进程（避免端口、容器名、Ray session 干扰）

### 5.2 Workload DoD（GRPO + SFT 都必须）

GRPO（必须）：

- [ ] `algorithm.adv_estimator=grpo` 的 job 可提交并进入 RUNNING
- [ ] job 能跑完最小训练步数（可设 `total_epochs=1` 或 `total_training_steps`）
- [ ] 输出目录内有日志与至少 1 次 checkpoint（或明确不保存并说明原因）

SFT（必须）：

- [ ] `sft_trainer_ray` 可连接集群并跑到至少 1 个 step（推荐最小训练步数/epoch）
- [ ] 输出目录与 checkpoint 策略同 v1.1 规范（落盘到 `${SHARED_ROOT}/jobs/<id>/...`）
