# 实施进度
> 配合 `DESIGN.md`。本文件只记 phase 状态、决策偏差、文件量、下一步。每条 2-4 句:做了啥 + 关键判断 + 没动什么;细节查 `git log` / `git diff`。
最后更新:2026-05-20(files API 加 copy/move 跨目录搬动:多选 + 弹框目录选择;move 闸住"顶层目录是某 task working_dir"维持 invariant)
---
## 状态
| Phase | 标题 | 状态 | 备注 |
|---|---|---|---|
| 1-3 | 骨架 + Skill + run_python | ✅ | 三个 skill;CoreCoder 唯一匹配 edit;敏感 env 过滤 |
| 4 | 演化性能力 | 🟡 | Model Profile + Probing ✅;版本化 prompt 未做 |
| 5 | Eval Suite | ⏸ 不做 | dogfooding 替代,probe 覆盖健康检查 |
| 6 | 长任务工程化 | 🟡 | task + 恢复 ✅;双层记忆 ✅;context 压缩未做 |
| 7 | 打磨 | ❌ | Docker 沙盒 / 更多 skill |
| §7 SaaS | DESIGN §7 路线 | 🟡 | A 事件流化 ✅;B 完工 ✅;**D `/v1` JSON API ✅**;**D' 过渡 auth(邮箱密码 + platform_key → JWT)+ dev SPA ✅**;**单活 run 锁 + cancel ✅**;**0004 schema 瘦身 ✅**(删 runs/usage_events);**入口归位 ✅**(`cli.py`→`main.py`,装配 lib 挪 `core/agent_builder.py`);真 OIDC 待;C(Executor)待。 |
---
## 已完成关键能力
### 2026-05-20
- **`POST /v1/files/copy` + `/v1/files/move` 跨目录批量搬动 + dev SPA 多选 + 目录选择弹框**:用户要"在文件夹间复制/移动文件"。后端两路由共用 `_validate_transfer` 预检 helper(批量原子校验:源存在、不能等于/含 dest、不在 dest 直接子级、批内重名、target 已存 409,任一失败整批 abort,无 FS 副作用)。**move 加额外闸**:任一源是顶层目录且为某 task `working_dir` → 409(维持"working_dir = 顶层目录"invariant — 允许沉到子目录后,rename 顶层只更新当前层 task 的 DB-aware 逻辑会失效,代码复杂度翻倍才能扛住嵌套场景;用户想归档项目目录:先 DELETE task)。**copy 无此闸**,新副本无 task 关联。dev SPA:`.file-row` 加 `` 列 + 顶栏 `#files-selall` 三态(全/半/无),选中 ≥1 出黄底 toolbar(`复制到…` / `移动到…` / `取消选中`)。目录选择弹框 `#dir-picker-modal` 复用 `/v1/files` 浏览(只列目录,面包屑可点回上层,源目录灰禁),底部按钮文案随 mode 切。`state.selectedFiles` 切 task / 切 filesPath 时清,refresh 后剔除已不存在的 rel 保 view 一致。**部分失败**:沿用现有 rename / delete 单向语义,FS 中途失败抛 500 + 已成功项保留(`shutil.move/copytree` 失败几乎只在跨卷断连 / 磁盘满,workspace 同盘罕见)。**没动**:DESIGN(API 添加非语义变更)、RUN(无 CLI / env 变化)、DB schema。
- **working_dir 视为可重生 FS 视图**:DB 是 source of truth,FS 目录可独立删 / 用户手动 rmtree / 跨机器迁移丢失,**下次跑就自动 mkdir 重建**。三处改:① `DELETE /v1/tasks/{id}` 删完后若同 user 下再无 task 引用此 working_dir 且 FS 目录为空 → best-effort `rmdir` 清孤儿(非空 / 不存在 / 外部 --working-dir 静默跳过)。② `POST /v1/files/delete` 顶层目录去掉「有 task 引用就 409」闸,允许独立删空目录,task.working_dir 字段不动。③ `core/agent_builder.py::build_agent` 把 `working_dir_path.mkdir(parents=True, exist_ok=True)` 从 `if not resume:` 里挪出,resume 也兜底建目录(用户手删 FS 后再 send message 不会炸)。smoke `scripts/smoke_files_rename.py` 增 case 4 (200 + working_dir 不变) / case 8 (DELETE task 空目录自动清) / case 9 (非空目录保留),全 9 pass。**没动**:DB schema、rename 顶层目录的同步 UPDATE 逻辑(rename 是明确改名,和"删后重生"语义不同)、外部 --working-dir(DB 绝对串)的清理(避免误删用户外部项目)。
### 2026-05-19
- **0006 模型切换(c 模式 task 级 A 粒度)+ usage_events v2 表**:`tasks.model_profile` 从死字段变 source-of-truth,顶栏下拉 → `PATCH /v1/tasks/{id}` 即换,**A 粒度下条 send 生效**(当前 run 不受影响;running 中切 UI 提示"跑完后生效")。`build_agent` resume 时优先 task.model_profile,新建 task POST body 加可选 `model_profile`(留空 → `cfg["default_model"]`)。`GET /v1/models` 扫 `config/models/*.yaml` 列可选项(含 display_name / thinking_mode / is_default),`ModelCapabilities` 加 `display_name` 字段,deepseek_v4.yaml 两 variant 各填名。**前端**:chat-meta 加下拉(切了 PATCH+提示)、新建对话框 modal 加 `