# MVP v3.0 进展记录（milestone log）

本文档用于记录 v3.0 按 `specs/mvp/v3.0/v3.0_dev_plan.md` 实施过程中的里程碑完成情况。  
约定：每完成一个里程碑，追加一条记录，包含**日期**、**完成内容**、**涉及文件**、**验证方式/结果**、**待办/风险**。

---

## M1：Path policy + tests（已完成）

- 日期：2025-12-30
- 范围：按 v3.0 路径策略升级 API submit 的路径校验（不扩展 TaskSpec YAML 结构）。
- 完成内容：
  - `code_path`：仍只允许 `/private/common/...`（v3.0 不执行 user code）。
  - `train_file`/`val_file`：允许 `/private/common/datasets/...` 或 `/private/users/<me>/datasets/...`。
  - `model_id`：若以 `/private/` 开头则视为本地路径，仅允许：
    - `/private/common/models/...` 或
    - `/private/users/<me>/models/...`
    否则仍按 HuggingFace repo id（如 `Qwen/...`）处理。
  - 拒绝跨用户路径（例如 `bob` 提交 `/private/users/alice/datasets/...`）。
  - 拒绝本地模型路径不在 `models/`（例如指向 `jobs/`）。
- 涉及文件：
  - `src/mvp/py/argus/service/app.py`
  - `src/mvp/py/tests/test_users.py`
- 验证方式与结果：
  - 本地单测：`.venv/bin/python -m pytest -q`
  - 结果：全部通过（`54 passed`），覆盖率阈值保持 `>= 90%`。
- 待办/风险：
  - `model_id=/private/...` 的“本地模型路径语义”需要在用户文档/WebUI 中明确提示（避免误用）。
  - 后续 M2/M3 需要把该路径策略同步到 UI 表单/提示文本（避免用户填错路径）。

---

## M2：SFTPGo 集成（方案 A：用户联动创建 + password）（已完成）

- 日期：2025-12-30
- 范围：SFTPGo（Data Management）最小集成 + 用户自助信息 `/api/v2/me` + 用户目录结构落盘。
- 完成内容：
  - 新增 `data` 配置段：
    - `data.user_root`：用户数据根目录（默认 `/private/users`）
    - `data.sftpgo`：SFTPGo 可选联动（enabled/host/sftp_port/admin_api_base/admin_user/admin_password_env）
    - `data.retention`：jobs 过期策略配置（3 天移入 trash，7 天 purge；janitor 在 M4 实现）
  - 新增 `SFTPGoAdminClient`（`urllib` 实现，不使用 `requests`）：
    - `create_user` / `disable_user` / `reset_password`（最小集合）
  - API server 增强：
    - `POST /api/v2/users`：创建 DB user + 同步创建目录结构（`datasets/models/code/jobs/trash/jobs`）
    - 当 `data.sftpgo.enabled=true` 时，创建用户会联动调用 SFTPGo admin API，并返回一次性密码（明文仅返回一次，服务端不保存）
    - `POST /api/v2/users/{user_id}:disable`：禁用用户（SFTPGo 禁用 best-effort）
    - `POST /api/v2/users/{user_id}/sftp:reset_password`：管理员重置一次性密码（SFTPGo enabled 才允许）
    - `GET /api/v2/me`：返回当前用户的目录约定、retention 提示，以及（可选）SFTP 连接信息
  - 同步更新 `src/mvp/configs/dev.yaml`：补齐 v3.0 相关 `data.*` 配置（默认关闭 sftpgo）。
- 涉及文件：
  - `src/mvp/py/argus/service/config.py`
  - `src/mvp/py/argus/service/sftpgo.py`
  - `src/mvp/py/argus/service/app.py`
  - `src/mvp/py/tests/test_sftpgo.py`
  - `src/mvp/py/tests/test_users.py`
  - `src/mvp/py/tests/test_app.py`
  - `src/mvp/py/tests/test_service_config.py`
  - `src/mvp/configs/dev.yaml`
  - `specs/mvp/v3.0/v3.0_api.md`
- 验证方式与结果：
  - 本地单测：`.venv/bin/python -m pytest -q`
  - 结果：全部通过（`62 passed`），覆盖率 `90.11%`（阈值 `>= 90%`）。
- 待办/风险：
  - M2 仅做了“API 侧联动 + 单测”，未在真实 SFTPGo 容器上端到端验证（按计划在 M5 完成）。
  - 目录创建依赖文件系统权限：生产部署时需确保 API/head 容器对 `/private/users` 可写。

---

## M3：WebUI（最小可用，多页面 + 侧边栏）（已完成）

- 日期：2025-12-30
- 范围：API server 托管最小 WebUI（同源，不引入 Node 构建），用于登录/提交/查看任务与日志、查看 data 信息。
- 完成内容：
  - 新增 UI 路由（HTML+少量 JS）：
    - `/ui`（重定向到 tasks）
    - `/ui/login`：token 粘贴并写入浏览器 localStorage（key=`mvp_token`）
    - `/ui/tasks`：任务队列列表（调用 `/api/v2/queue`）
    - `/ui/tasks/new`：提交 TaskSpec YAML（POST `/api/v2/tasks`）
    - `/ui/tasks/{task_id}`：任务详情（GET `/api/v2/tasks/{task_id}`，支持 cancel）
    - `/ui/tasks/{task_id}/logs`：日志查看（GET `/api/v2/tasks/{task_id}/logs`，可选自动刷新）
    - `/ui/data`：展示 `/api/v2/me` 返回的路径/SFTP/retention 信息
  - 统一侧边栏导航：Tasks / New Task / Data / Login。
  - UI 不做服务端 session：所有 API 调用均由浏览器带 `Authorization: Bearer <token>`（localStorage 注入）。
- 涉及文件：
  - `src/mvp/py/argus/service/ui.py`
  - `src/mvp/py/argus/service/app.py`
  - `src/mvp/py/tests/test_ui.py`
- 验证方式与结果：
  - 本地单测：`.venv/bin/python -m pytest -q`
  - 结果：全部通过（`65 passed`），覆盖率 `90.53%`（阈值 `>= 90%`）。
- 待办/风险：
  - WebUI 当前为“骨架+API 驱动”，不做复杂交互与大文件下载；上传/下载仍以 SFTP 为主（按设计）。
  - Starlette TestClient 的 `allow_redirects` 有弃用告警（不影响功能，可在后续清理）。

---

## M4：Jobs Retention janitor（3 天移入 trash，7 天后 purge）（已完成）

- 日期：2025-12-30
- 范围：API server 内置后台线程，对“已结束 attempt”的 job 目录执行保留策略（文件系统直连，不依赖 SFTPGo）。
- 完成内容：
  - 新增 `JobsJanitor`：
    - 以 `attempts.end_time` 为基准计算 TTL（从 job 结束开始算）
    - `>= 3 天 && < 7 天`：把目录从 `.../jobs/<ray_submission_id>` 移动到 `.../trash/jobs/<ray_submission_id>`
    - `>= 7 天`：确保目录进入 trash 后删除（`shutil.rmtree`）
    - 对缺失目录、异常移动/删除为 best-effort（不影响服务主流程）
  - DB 增强：新增查询 `list_ended_attempts_before()`，用于 janitor 扫描候选 attempt。
  - API server 启动时启动 janitor 线程（可通过 `data.retention.janitor_interval_s` 控制；<=0 视为关闭）。
- 涉及文件：
  - `src/mvp/py/argus/service/janitor.py`
  - `src/mvp/py/argus/service/db.py`
  - `src/mvp/py/argus/service/app.py`
  - `src/mvp/py/tests/test_janitor.py`
- 验证方式与结果：
  - 本地单测：`.venv/bin/python -m pytest -q`
  - 结果：全部通过（`75 passed`），覆盖率 `90.72%`（阈值 `>= 90%`）。
- 待办/风险：
  - M4 只做“逻辑 + 单测”，实际 `/private/users/...` 的权限与在 `argus@h1` 的行为验证放到 M5（端到端）。

---

## M5：端到端（h1）— SFTPGo compose + v3.0 E2E 脚本（已完成：交付脚本/配置）

- 日期：2025-12-30
- 范围：补齐 h1 端到端所需的 compose/service、配置与一键脚本（实际运行/验收由你在 `argus@h1` 执行）。
- 完成内容：
  - SFTPGo 集成到 `docker compose`：
    - 新增 `argus-sftpgo` service（SFTP 2022；Admin API/UI 8080→host 8081，避免与 MVP API 8080 冲突）
    - 同挂载 `../../shared:/private`，并持久化元数据到 `../../shared/common/sftpgo`
  - SFTPGoAdminClient 实装（对齐 upstream OpenAPI）：
    - `GET /api/v2/token`（BasicAuth）获取 admin token
    - `POST /api/v2/users` 创建用户（含 `permissions: {"/":["*"]}`）
    - `PUT /api/v2/users/{username}` 禁用/重置密码
  - 新增 v3.0 dev 配置：`configs/dev_v30.yaml`（启用 `data.sftpgo` 并配置 `admin_api_base=http://argus-sftpgo:8080/api/v2`）
  - 新增 v3.0 一键脚本：
    - `scripts/run_all_v30_api.sh`：起 Ray+SFTPGo、启动 API、创建用户并提交 PPO/GRPO/SFT（引用 user dataset 路径）
    - `scripts/run_e2e_v30_cases.sh`：最小 E2E runner（HP-1）
  - API 启动脚本增强：`scripts/60_start_api.sh` 支持透传 `SFTPGO_ADMIN_PASSWORD` 到 head 容器内的 API 进程。
- 涉及文件：
  - `src/mvp/docker-compose.yaml`
  - `src/mvp/configs/dev_v30.yaml`
  - `src/mvp/scripts/run_all_v30_api.sh`
  - `src/mvp/scripts/run_e2e_v30_cases.sh`
  - `src/mvp/scripts/60_start_api.sh`
  - `src/mvp/py/argus/service/sftpgo.py`
  - `src/mvp/py/tests/test_sftpgo.py`
  - `src/mvp/README.md`
  - `specs/mvp/v3.0/v3.0_api.md`
- 验证方式与结果：
  - 本地单测：`.venv/bin/python -m pytest -q`
  - 结果：全部通过（`75 passed`），覆盖率 `90.35%`（阈值 `>= 90%`）。
- 待办/风险：
  - 需要你在 `argus@h1` 实跑 `scripts/run_all_v30_api.sh` 完成真正的 SFTP 上传/下载与 retention 验收（按 `v3.0_acceptance.md`）。
