# 实施进度 > 配合 `DESIGN.md`。本文件只记 phase 状态、决策偏差、文件量、下一步。每条 2-4 句:做了啥 + 关键判断 + 没动什么;细节查 `git log` / `git diff`。 最后更新:2026-05-19(dev SPA 登录从"邀请码/uuid5"撤回 邮箱+密码 — `users.email/password_hash` + UNIQUE + `main.py user add` CLI + 登录页两 tab) --- ## 状态 | 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-19 - **dev SPA 登录撤回 邮箱+密码,删 invites 表**:前两条"邀请码 env → invites 表(0005)"一日游撤回,复用 users 表本来就有的 email/password_hash 列(0001 schema)+ 0005 加 UNIQUE(email)。`bcrypt` 哈希,新 `/v1/auth/login_password` 路由,新 `main.py user add --email --password` CLI 发用户。dev SPA 登录两 tab(邮箱密码 默认 / UUID+PLATFORM_KEY 备用,last-used 持久化 LS)。判定:邀请码 uuid5(NS,name) 推导对外是黑盒(改 name = 换身份),复用 users 列语义清晰也对齐生产路径。**没动**:JWT 签发 / platform_key 路径 / DB users 表列结构。 - **邀请码后端 env → invites 表(0005)** _(已撤,见上条;原条目已删,有需要看 git history)_ - **SENTINEL user 彻底撤(数据 + 代码)**:`SENTINEL_USER_ID = UUID('00000000-...')` 在 web 必从 JWT 拿 user_id 后已无角色,按 CLAUDE.md "不写兼容层" 连根拔。DB CASCADE 删 sentinel user + workspace dotfile 目录;代码 10 处删 import / 默认参数 / fallback,`utils.py` 三函数和 `build_agent` 的 `user_id` 从可选变必填(`build_agent` 加 `*,` 转 KEYWORD_ONLY 规避默认参数顺序)。**Bonus**:把"操作 user 数据的函数必须显式传 user_id"作为 Python 必填参数固化,以后多 user 函数 typechecker 会拦到。 - **dev SPA 邀请码登录(env 形态)** _(已撤,见 SENTINEL user 撤之后两条,路径整体改邮箱密码)_ - **任务/文件行 `⋯` 下拉菜单 + 文件顶栏长名截断 + 聊天框上传按钮 + 工具调用 debounce 刷新右侧**:单例浮层菜单(`#floating-menu` position:fixed)避开 pane overflow 裁剪。任务行 4 项(complete/abandon/export/delete,不同颜色,非 active 自动 disable);文件行 3 项(改名/下载/删除);聊天框加上传按钮共用 ``;`tool_result` 事件 debounce 500ms 刷新文件 panel。仅前端,不动后端 / DESIGN / RUN。 - **proposal skill mermaid hash→caption + quality_check 加图相关 4 拦截 + SKILL.md 精简 + `/v1/files/download` 加 `Cache-Control: no-cache`**:用户反馈"申报 skill 图没渲染到 docx",诊断双层 bug:① 模型写满 ASCII 字符画从未用 mermaid + `![]()`;② SPA 预览命中浏览器启发式缓存(Starlette FileResponse 无 Cache-Control)。修法:render_diagrams 改 caption 强制必填 + 同 task 唯一(撞名退 2);quality_check 加 4 条(figures/ 有 png 但 sections 0 引用 / 围栏含 box-drawing 字符 / mermaid 缺首行 `%% caption:` / caption 撞名);SKILL.md ~193→~160 行。 - **dev SPA 文件预览弹框**:点击文件不再直接下载,弹 90vw 模态按扩展名分派(image/pdf/text/md→已有 renderMd / docx 用 docx-preview / xlsx 用 SheetJS / pptx 等 fallback "请下载查看")。库懒加载 + blob URL 全局 track + 弹框关时 revoke 防漏;vendor 入 git(jszip / docx-preview / xlsx,~1MB,无 npm 链路就直 vendor 锁版本)。**没动**:后端 app.py(blob URL 路径足够)。 ### 2026-05-18 - **入口归位:`cli.py`→`main.py`,原 `main.py`→`core/agent_builder.py`,删 CLI REPL,§7 E 撤**:`main.py` 混三角色(装配 lib + utility + cli/web 共 import 的事实入口),按 SoC 拆。`git mv` 两次(覆盖)+ 5 处 `from main import` → `from core.agent_builder import`。删 `chat / tasks / export` 三命令 + REPL 主循环 + 内部 helpers(~400 行);新 `main.py` 只剩 `db / probe / web`(后来再加 `user`)。失:CLI 无 auth 直跑 core 通道;补:dev SPA 走同条 web 路径,临时调试写几行 ad-hoc script。 - **0004 schema 大瘦身:删 runs / usage_events,合 run_status / run_error 入 tasks;路由 run_id → task_id**:`usage_events` 全代码库零写零读,`runs` 表 tokens_p/c 写但从未读(真 tokens 走 tasks 累计),started_at/finished_at/error 也只写不读,`run_id` 唯二实用是 broker 频道键 + cancel 参数 — 单活 run 形态下客户端只需 task_id 就够。`tasks` 加 `run_status text default 'idle'`(idle/running/cancelling/error,error 是唯一持久终态)+ `run_error text`。Broker 全 task_id 索引 + 加 `start(task_id)` 清上轮 done 标记。**dev SPA**:`state.currentRunId` → `state.streaming` bool;cancel POST `/v1/tasks/{tid}/cancel` 去 `/runs/{rid}/`。 - **`POST /v1/files/rename` + 顶层目录 delete 加 task 引用闸**:**`/v1/files/*` 升格为唯一目录树 mutation 入口,DB-FS 一致性作服务端不变量内化**;`GET /v1/folders` 定位"项目聚合视图",只读。顶层目录(`target.parent.resolve() == root.resolve() and is_dir()`)走 DB-aware 分支:事务内 `SELECT ... FOR UPDATE` 锁关联 task + 任一 running/cancelling → 409 + `check_no_subtask(exclude=被改名 tids)` 防嵌套 + UPDATE 在 FS rename 之前(FS 失败可回滚)。**架构教训(§7.9)**:此前提的双命名空间 `/v1/folders/rename` vs `/v1/files/rename` 反了 — `is_top_level` 分支是**从数据状态派生**(path 恰好是 working_dir),不是客户端意图派生,放服务端是更安全的位置。 - **task-level cancel + AgentLoop 协作式 cancel + dev SPA stop 按钮**:Broker 加 `request_cancel / is_cancelled / clear_cancel`(per-task `threading.Event`,`setdefault` 保证 BG 还没 register 也能 set)。Loop 加 `cancel_check` callable + `_fill_cancelled_tool_results` 给未执行 tool_call 补 `[cancelled]` tool message(LiteLLM 协议要求 assistant tool_call 必须有匹配 tool result,否则 resume 报错)。**LLM 同步 call 本身不可中断**(litellm 阻塞,无原生 cancel)— 最坏等当前一轮跑完几十秒。Gate 同步扩:`post_message` 单活 run 检查 `status in ('running', 'cancelling')` 避免新旧 BG 撞 messages.idx。 - **`POST /v1/tasks/{id}/messages` 单活 run 锁 + 孤儿 reaper**:同事务 `SELECT Task ... FOR UPDATE` + 活跃状态检查 + 标 running,三步原子完成避免 TOCTOU(用户连点 send / 多 tab 同时发 → 两 BG 线程争 `messages.idx`)。lifespan 加 reaper:启动时 `UPDATE Task SET run_status='error' WHERE run_status IN ('running','cancelling')` 清进程 crash 留下的孤儿。**未来 TODO**:multi-worker 部署 reaper 不能简单全表清(会误清其他 worker 的真在跑),换 heartbeat + lease。 - **proposal skill 流程图/结构图管线**:`render_diagrams.py` 扫 sections/*.md mermaid 块 → mmdc(本地)或 mermaid.ink(公网) → png;render_docx 加 `add_picture` 识别 `![](...)` 单行 + mermaid 围栏特判;templates 三处占位换成完整 mermaid 例子。图编号 `ctx['fig_no']` 调用链递增不重不漏;mmdc/网络都没的极端环境 docx 仍能产(ASCII 退化)。 - **system prompt skill 机制改"可选辅助"**:接 GET /v1/skills + 下拉落地后,prompt 第 14 行从 `"永远 load 一下"` 改 `"简单问答/读代码/改 bug/文件操作直接用通用工具就够,不必为每个任务硬套 skill"`;一旦决定要用仍 load 完整指引。**Tradeoff**:边缘场景(用户提"整理大纲")agent 偏向不 load 可能漏掉好的模板,比"什么都套 coding"的噪音更可接受。 - **`GET /v1/skills` + dev SPA skill 字段改下拉**:lifespan 启动 `SkillRegistry` 扫一次挂 `app.state`(FS 静态运行中不变);返 `{skills:[{name,description}]}` 按 name 升序。前端 `` 换 `