# 实施进度

> 配合 `DESIGN.md`。本文件只记 phase 状态、决策偏差、文件量、下一步。每条 1-2 句:做了啥 + 关键判断;细节查 `git log` / `git diff` / `DESIGN §7.9`。

最后更新:2026-06-11(用户私有 skill:多来源 registry + save_skill/fork_skill + skill-creator)

---

## 状态

| Phase | 标题 | 状态 | 备注 |
|---|---|---|---|
| 1-3 | 骨架 + Skill + run_python | ✅ | 多 skill(coding/proposal/ppt/research/documents/imagegen/videogen/review/patent);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 + dev SPA ✅;单活 run 锁 + cancel ✅;0004 schema 瘦身 ✅;入口归位 ✅;真 OIDC 待;**C Step 1-3 + 3d ✅(Executor + Docker 池 + DockerExecutor + fs 工具进容器)+ Step 5 部署前置对账 ✅ + 容器资源 yaml + 应用层磁盘配额 ✅ + dogfood 网络放开 + 容器内 pip/npm 源持久化 ✅**;**Step 4 完整 egress proxy + Step 3b PGID kill 协议延后到外部用户开放前**(还需 egress proxy + xfs project quota OS 层硬化,§7.5 落地清单 #2 #4)。 |

---

## 已完成关键能力

### 2026-06-11

- **dev SPA「技能」查看 modal(左侧 rail 底部入口)**:因 `.skills` 在文件面板隐藏,加左侧 rail 底部「我的资源」分组(`#rail-resources`,留位给后续「记忆」)+「技能」按钮 → 弹 modal 分「平台 skill / 我的 skill」两组列表,点任一项展开**完整 SKILL.md**(`GET /v1/skills/{name}` + 现有 markdown 渲染),「我的」每项带删除(二次确认 → `DELETE /v1/skills/{name}`,只删 user 源 + 防穿越);覆盖标 `已覆盖平台同名`,`load_errors` 提示未加载的。创建/改/fork 仍走对话。新 `web/static/js/skills.js`(零构建 ES module,main.js import + Esc 栈接入);`/v1/skills` 已带 source/overrides/load_errors。**纯查看 + 删除,不在 UI 做创建/编辑**(编辑天然对话式)。
- **用户私有 skill(每用户 `.skills/`,可从零写或 fork 内置再改)**:`SkillRegistry` 从单目录改**多来源**(`SkillSource` 列表:内置 `ROOT/skills` + 用户 `user_root/.skills`),后扫同名覆盖先扫 → **user wins**;覆盖关系记进 `user_overrides`,discovery 显式标 `[你的·已覆盖内置]`(不静默)。`Skill` 加 `source` 字段;`from_dir` 区分"无 SKILL.md(静默跳过)"与"有但格式错(抛 `SkillLoadError`)",`_scan` 捕获用户来源的错收进 `load_errors`、注入 system prompt 提示用户修(一个坏 skill 不再崩整次扫描)。容器路径改写从 LoadSkillTool 下沉到 registry(`container_dir` 按 `source` 给 `/sandbox/skills` 或 `/workspace/.skills`),LoadSkillTool 去掉 `container_skills_dir` 参数。**关键判断**:写 skill 用 host-side typed tool(`save_skill`/`fork_skill`,`tools/skill_authoring.py`)而非 fs/shell —— 因 fs 的 base_dir 锚 cwd(host)/ 容器 wd(docker),都够不到 `user_root/.skills`,跨 backend 不可靠;host-side 工具知道 user_root 一个落点两模式通吃(与 seedream/DocumentDownload 一致范式)。`save_skill` 写时校验 frontmatter(名合法 / YAML 合法 / 有 description / name 一致),`fork_skill` copytree 整目录(带脚本)+ 自动把 frontmatter name 对齐新名(否则 fork ppt 仍叫 ppt 会反覆盖内置)。`.skills` 是 dotfile(文件面板隐藏,与 `.memory` 一致;`validate_task_name` 已禁 `.` 起头 working_dir,天然不撞)。`/v1/skills` 带上用户 skill + `source`/`overrides_builtin`/`load_errors`。新增 `skill-creator` 引导 skill。+`test_user_skills.py`(20 例)+ 改写 `test_load_skill.py`。性能:多扫一目录,没 `.skills` 的用户一次 `exists()` 跳过;有 skill 仅每 run +1-3ms,不在热路径。

### 2026-06-10

- **system prompt 精简(瘦身 ~40 行 + 媒体段按需注入)**:`general_v1.md` + `_build_system_prompt` 去冗余:① 「宪法」文件命名约定从 ~25 行压到 ~6 行(只留格式定义 + 注入值 + 一行 current/重定调,操作细节本就由 proposal/ppt skill 各自讲,引用仍成立);② run_python「先 write script 再 script_path」指引去重(原模板 + agent_builder 两处 → 合并进模板 1 处,顺带把 `scripts/` 子目录约定收进去);③ 媒体工具段(seedream/seedance 红线)从常驻模板抽成 `_MEDIA_TOOLS_BLOCK`,仅 `ArkConfig.load() is not None`(有 ARK_API_KEY)时由 agent_builder 追加——无 key 用户不再背 7 行永远报错工具的说明,且 ark_cfg 提前 load 一次复用给下方 tool 注册;④ 「路径 echo 全形式」段 8 行压到 4 行。通用任务每轮 system prompt 净瘦 ~40-50 行,领域 task 加载 skill 后信息不丢。`test_system_prompt_paths` 仍过。
- **上下文压缩加压力门槛**:压缩只在总 chars 超阈值(`caps.reliable_context×0.5×2.5 char/token`,flash ≈33 万)时才做,未超则原样发——护 DeepSeek 前缀缓存(短任务字节逐轮一致、全程命中)+ 不白丢旧细节。`prepare_messages_with_stats(compact_threshold_chars=)`,`compaction_skipped` 进事件;默认 0=向后兼容永远压。实测高轮 task 缓存命中已 92-94%,故只补门槛不改滑动边界。+2 测试。
- **单轮停机判据从「步数」解耦为「是否在推进」**:`max_iterations` 从「轮预算」降级为纯安全 backstop(flash 50→120 / pro 100→150),真正掐空转靠两道进展信号——`_RepeatGuard` 逐指纹「无产出重复」累计(SOFT2 注提示 / HARD4 拦截)+ run 级全局 `_stall`(整步所有 tool 无净产出 +1、任一净产出清零,连续 8 步主动停)。撞 backstop / 熔断都 emit「回复『继续』可续跑」提示,不静默停。(诊断:task `b27466a0` 所谓「中途断」实为撞旧 50 步上限干净停下。)
- **`systemctl restart` 优雅 drain in-flight run**:restart 不再硬杀 BG run 致 reaper 误标 error。纯进程内零 DB 改动:lifespan 加 `draining` + `inflight` 登记,先拒新 run(503+Retry-After)再 `asyncio.wait(drain_timeout)` 收尾,超时转协作式 cancel。部署强耦合:unit `TimeoutStopSec` 提到 90(必须 > drain+grace),前端发送包退避重试。

### 2026-06-09

- **PPTX 前端在线预览(LibreOffice→PDF,DESIGN §8.3 Stage 1)**:文件区点 `.pptx` 改在线预览。关键洞察=前端已有 PDF iframe 路径,所以后端把 pptx 转 PDF 即可前端几乎不动。新 `web/pptx_render.py`(soffice 转 PDF,独立临时 profile 绕单 profile 锁 + 缓存 `.preview/<hash>.pdf` + 超时 kill)+ `GET /v1/files/preview_pdf`(复用鉴权防穿越 + per-path lock + run_in_executor)。转换在 web host 进程不进沙盒;部署装 libreoffice-impress + noto-cjk。
- **药3 复核:`/home/ubuntu/zcbot` 幽灵路径不复现 + 回归测试钉死**:该路径(docker 下 system prompt 焊死宿主路径,容器内找不到致 51 次重试风暴)已于 06-03 修复,复核当前代码 docker 分支只注入容器路径不泄漏宿主路径/uid。加 `test_system_prompt_paths.py`(2 例)防回归。高轮数三味药全部收口。
- **ppt skill 补「信息设计纪律」+ 混合背景 + pptx 预览器**:深读 pptmaster 后定位 ppt 观感差真因是信息设计纪律(~70%)非 SVG 渲染(~30%)——且这些全是 editable python-pptx 能做的。加 `add_takeaway`/`add_kpi(baseline+delta)`/`add_source`/`add_toc` + 组合件 `add_card_grid`/`add_timeline`/`add_cycle` + `render_bg.py`(Chrome 渲 mesh 渐变背景)+ `pptx_preview.py`(渲 PNG 肉眼验观感,当场抓到 set_text 多行只给首段上色的 bug);投影改克制(`add_card` 默认不投影)。**未动**:SVG→原生转换器(论证零增益)。

### 2026-06-08

- **loop 加病理性重复调用守卫(药1)**:`_RepeatGuard` 按 `(工具名, canonical 参数)` 指纹跟踪「无产出重复」——结果每次不同(改脚本重跑)算有产出、清零永不误伤;结果是 `[Error]` 或一字不差才累计;SOFT2 注软提示、HARD4 拦截。顺带堵 `_malformed_tool_calls` 退化成空 `{}` 的风暴。+`test_loop_repeat_guard.py`(7 例)。
- **检索/抓取类 host 工具批量化**:DB 实测高轮数烧 token 三股根因(空 `{}` 风暴 / 报错重试 / 检索不收敛)。把 `web_fetch`/`document_search`/`document_download` 从单数改列表入参、一轮并发处理一批(批内去重 + 单条失败隔离 + 超量截断明示),直接换签名不留单数别名。
- **ppt skill 视觉系统升级为卡片式**:学 ppt-master 后岔路三选,选 B(升级 python-pptx 设计系统,非自建 SVG 转换器——保留单脚本批量架构、原生可编辑)。`pptx_helpers` 加 add_card/gradient/kpi 等质感件 + 派生明暗色阶,layouts 扩到 13 版式,quality_check 按色相归桶对齐三色制。
- **system prompt 加「少来回」全局原则**:互相独立的操作合到一个脚本 / 一轮并发 call(轮数=token 线性乘数),但需看上一步结果的就老实分步。便宜补充(走缓存),不指望动 100+ 轮大头。
- **ppt skill 工作流批量化**:阶段二从逐页(每页一 run_python,~2N 轮)改成写一个 `build_deck.py` 一次建整 deck + 图标全 deck 批量预取,逐页大纲表替代逐页确认。N 页降到 ~3-4 轮。

### 2026-06-06

- **前端模块化 Step 2 收官**:把 main.js 剩余主体按干净度逐个剥成独立 ES module——layout / auth / preview / files / media / newtask / embed / chat(对话视图,合一个 chat.js 而非强拆 tasks+stream,因二者共享 state.liveRuns + run 生命周期)。main.js 2719→75 行入口;靠 ES live binding 解 main↔模块循环依赖;新增 import/export 一致性 + 从 main BFS 可达性校验。逻辑零改动纯剪切+连线。
- **修 deepseek-v4-flash 大参数工具调用 arguments 损坏 → loop 畸形重试**:大参数(7-10K)write/run_python 偶发把碎片错位粘进 `arguments` 致 json 解析失败;真正放大成灾的是 loop 把损坏消息入库 + 每轮重发的投毒级联。`_stream_llm` 改「校验 tool_call arguments 能否 json.loads,不能则丢弃整轮(不入库)重 roll,最多 3 次,最后降级非流式」+ executor 缺参早返友好提示替掉暴露签名的 TypeError。

### 2026-06-05

- **前端模块化 Step 1**:`web/static/dev.html` 4087 行单文件起步拆零构建 ES module(定方案「1 拆文件 → 2 局部引 petite-vue → 3 永不上 Vue+构建链」)。本步抽 5 个无依赖叶子(state/format/dom/api/markdown),主体落 main.js,`app.py` 加 `mimetypes.add_type` 兜底。逻辑零改动。
- **改密码弹框样式修复**:`#chpw-modal` 复用「选入文件」弹框头/体/脚布局,纯 CSS。
- **run_python 过程脚本约定 `<task_dir>/scripts/`**:显式写文件再 `script_path` 跑的过程脚本落 `scripts/`(可见/可重跑),inline `code` 匿名片段维持临时用后即焚。改系统提示 + tool 描述。
- **新增 `standard` skill(国标/行标/团标起草)**:核实市面无可复用 skill,据 GB/T 1.1—2020 自建。覆盖三层级(重点对接 CSTM 团标)× 两体裁;渲染复用 proposal `render_docx`/`render_diagrams`;quality_check 对标准误报无跳过开关 → 改 drafting_rules §8 人工 12 条清单。

### 2026-06-04

- **ppt 版式 helper 收进可 import 模块 + 修中文字体没真生效**:抽出 `pptx_helpers.py`(每页 `import P` 免默写 150 行 + 治长 deck 坐标漂移);字体修复=`set_text` 同时写 latin=Arial + ea/cs=微软雅黑(python-pptx `font.name` 只写 latin 是中文不生效根因)。
- **ppt `quality_check.py` 加内容形状重叠检测**:纯数值两两包围盒,只检有文字/图片的内容形状(装饰元素天然排除),交叠 ≥25% 才报。
- **ppt `quality_check.py` 配色纳入形状填充色 + 改三色制判定**:加 `_shape_fill_hex`,粗阈值「≤5 色」改「非灰阶色 ≤3」(`_is_neutral` 排中性色),否则合规红 deck 狂报假阳。
- **前端顶栏展示用户已用存储**:`GET /v1/user/storage`(复用 `user_disk_usage` 表),右侧文件面板底部钉进度条;不限额只显已用。
- **sandbox 容器 env 收编到一处 + shell 也注入**:`executor_docker` 抽 `_CONTAINER_ENV={PYTHONPATH=/sandbox:/workspace, HOME=/tmp}`,shell/run_python/fs 三路共用(修 shell 里 import skills 报错 + 只读 rootfs 下缓存写不进的噪音)。纯代码改重启生效。

### 2026-06-03

- **修 docker sandbox 下 system prompt 焊死宿主路径**:docker backend 时工具在容器跑但 `_build_system_prompt` 注入的是宿主绝对路径(容器内不存在),LLM 据此 find 全空。docker 下 `task_dir` + 宪法 glob 范例换容器路径 `/workspace/<wd_rel>` + 去掉无意义 cwd 行;host 不变。
- **顺扫清掉 SKILL.md 里残留的宿主路径假设**:patent 跨 skill 调 proposal 脚本改兄弟相对路径;research/patent/proposal/ppt 的硬编码 `D:/projects/zcbot` 与废弃旧布局举例改双形态说明。
- **修 ppt 图标缓存写进只读挂载**:种子图标库降为只读(glob 读),`fetch_icon.py` 新拉图标一律 `-o <task_dir>/assets/icons/`(与「产物只写 task_dir」一致)。
- **默认镜像源改清华(pip+apt)/ 腾讯(npm)**:腾讯 PyPI 吐损坏 litellm wheel(镜像端文件损坏)。
- **回退 `ZCBOT_WORKSPACE_DIR` env 覆盖,workspace 落数据盘改用 bind mount**:env 覆盖与 `paths.py` 锚 ROOT 的相对存储冲突致三家分叉,改 bind mount(`/data/zcbot/workspace`→`ROOT/workspace`)。

### 2026-06-02

- **【已于 06-03 回退】`resolve_workspace` 加 env 覆盖 `ZCBOT_WORKSPACE_DIR`**:prod 想 workspace 落独立数据盘,回退因与相对存储锚点冲突。
- **修 embed 模式「登录页一闪而过」**:`<body>` 首行加同步内联脚本,`?embed=1` 立即加 `embed-mode` class 赶在 `#login` 绘制前隐藏。绘制时机问题非鉴权。

### 2026-06-01

- **`deploy/update.sh` 加自更新重跑守卫**:`git pull` 改脚本自身时 `exec` 用新版本从头重跑(标记防死循环)。
- **`deploy/update.sh` 默认源改腾讯 + build 跳过改 `--skip-build` + 进度可见**:根因=阿里 PyPI 同步滞后缺 `litellm>=1.83`。
- **修 MP host 工具的全量下载(IP 被封根因)**:`mp_search_summary` 没传分页致每搜一次整库级下载被 MP 判 abusive 封 IP;改 `num_chunks=1` 服务端限量。(宿主 IP 仍需邮件 support 解封。)
- **加一键部署脚本 `deploy/update.sh`(Ubuntu/systemd)**:`git pull → pip → db upgrade → docker build → restart → curl /healthz`;钉死两点:migration 从 .env 抠 `ZCBOT_DB_URL`、build 必须在 restart 之前。
- **sandbox 镜像加中文字体**:Dockerfile slim 起一个 CJK 字体没装致 matplotlib/mermaid 出中文方块,加 `fonts-noto-cjk fonts-wqy-microhei`。
- **documents / Materials Project 带 key 能力改 host-side tools,key 不进 sandbox**:新增 `tools/documents.py` + `tools/materials_project.py`,仅宿主 env 有 key 时注册,写文件绑 task_dir。
- **删 `skills/pymatgen/materials.py::mp_rester()`**:sandbox 内读 key 的旧入口,host tool 化后多余且违背「key 不进 sandbox」。

### 2026-05-29

- **Seedream 5.0 i2i base64 通路 probe + DESIGN §8.1 落册**:实测 `/images/generations` 接受 base64 data URL → 内网部署无需对象存储中介。选 E+C 组合,本版仅 probe + design,tool 改造未启动。
- **web 端 tool_call 标题行改显中文活动描述**:修读错字段(`arguments` vs `args`)+ `toolActivityLabel` 按 12 工具套中文动词。

### 2026-05-28

- **`skills/review/SKILL.md` 加「长文档处理」段**:阶段 1 骨架扫描(停下等用户挑章节)→ 阶段 2 分章深审 + 中间文件落盘。
- **新增 `config/models/local.yaml`(family=local,r1/qwen3)接内网 OpenAI 兼容服务,涉密专用**:关键 `thinking_mode=false`(R1/Qwen3 天生推理,发 reasoning_effort 本地 vLLM 多半 400);不改默认模型。qwen3 跑通,r1 调试中。
- **修 `LoadSkillTool` 在 docker backend 返 host 绝对路径**:加 `container_skills_dir` 参数,docker 时返 `/sandbox/skills/<name>`,references-heavy skill 自动 work。
- **新增 `analyze` skill(科学问题分析/拆解/引导)**:四段式 PICO→Issue Tree→分支(Fishbone/First-principles+TRIZ/DoE)→路线图,定位协调器不执行任务,接力下游 skill。
- **Python 3.10→3.12 升级(host + Dockerfile)**:mp-api 依赖链 `NotRequired`(3.11+)在 3.10 import 不进;顺手修 `executor_docker` PYTHONPATH `/workspace`→`/sandbox:/workspace`。
- **新增 3 个科学计算 skill(pymatgen / stats_ml / plot_pub)**:服务无机非金属材料 R&D。pymatgen 带 `CEMENT_PHASES` 中英文相名映射 50+;stats_ml 纯指南;plot_pub 带 `apply_pub_style()` 出版级中文字体 fallback。挑 4 个 ★★★ fork 单装。
- **DESIGN §7.5 增「image 体积 / 多 user 资源 / 后续加包策略」决策段**:① image 大 ≠ 吃更多 RAM(layer 共享);② 多 user 瓶颈在并发 exec 不在 idle 容器;③ 新增依赖走「base 收敛 + per-user 持久化 venv + 使用频次沉淀」。

### 2026-05-27

- **ppt skill 歧义反问 + general_v1 加「产物形式歧义先问」通用原则**:「汇报方案」被误路由成 PPT,改先反问「PPT 还是文档」并升格到 system prompt 让新 skill 继承。
- **ppt skill description 收紧路由**:改显式白名单(PPT/幻灯片/.pptx/slide/deck)+ 显式反例(报告/文档/纪要不触发)。
- **skill 热更新:`/v1/skills` 每次现扫**:原启动扫一次须重启;改每次现扫(~3ms)。

### 2026-05-25

- **dev SPA 前端依赖 CDN 本地化 + 升级稳定版**:markdown 渲染(marked/dompurify/highlight.js)从 jsDelivr 改本地 `vendor/`,避免内网/跨境 CDN 抖动;`test_static_vendor.py` 回归。
- **dev SPA 一批上传/布局交互打磨**:三类上传入口改 XHR 显进度 + 粘贴 chip 可预览可删;三栏右栏折叠 + 分隔线拖拽调宽(LS 持久化)。
- **接入博查 Web Search + Web Fetch**:`web_search.py`(Bocha,仅 env 有 key 挂)+ `web_fetch.py`(httpx + html2text,SSRF 内网屏蔽)。

### 2026-05-22

- **dev SPA 加 iframe embed 模式(`?embed=1&parent_origin=`)**:父页 postMessage 握手拿 JWT,`event.origin` 双向白名单,`PLATFORM_KEY` 不下发浏览器;`web/EMBED.md` 对接手册。
- **embed 模式接受 `task_id` URL 参数定位 task**:首次签发后 `selectTask`,`once` 标记只生效一次(401 重签不重置用户中途切的 task)。
- **媒体生成每账号每日配额(yaml 可配,默 20 图/5 视频)**:`check_daily_quota` 按服务器本地今日计,超额返中文提示不烧钱;tool 返串不贴 yaml 路径防泄漏 schema。
- **对外路径协议刚性化**:`general_v1.md` 规定助手 echo 产物路径用 user_root 相对全形式 `<wd_name>/<rel>`(简写致 chip 失效),跨产物 skill 统一;UI 一次性兼容历史简写。
- **豆包 Seedance 2.0 Fast 视频生成接入(文生视频)+ videogen skill**:`tools/seedance.py`(ark 建任务→轮询→download mp4,失败/cancel 不计费);build_agent 加 `video_variant` + cancel_check;skill BLOCKING 门槛更严(¥4 vs ¥0.22)。phase 1 仅 t2v,fast 上限 720p。
- **dev SPA 移动端自适应 + 交互打磨**:手机两档断点(平板 rail / 手机单列 `.mobile-tabs`,`100dvh` 解 iOS、输入 ≥16px 防缩放);chat-input 支持 Ctrl+V 粘贴上传。

### 2026-05-21

- **dev SPA UI 打磨**:修 primary 按钮 hover 文字消失;4 个 modal 抽 `.modal` 基类(style 589→522 行);新建任务/filter 工作目录回原生 `<select>` + sentinel + 二级 input(combobox 试过推翻)。
- **sandbox 阻塞地位写进 DESIGN(§7.7/§7.8/§7.9)**:`shell.py::BLOCKED_PATTERNS` 是 trivial-bypass 装饰品(命令注入图灵完备),不继续加规则;正确防线在 OS 层 §7.5。本地 dogfood blast radius 限自身;SaaS 外部开放才 hard prereq。
- **system prompt 注入 task 预选 skill 提示**:`_build_system_prompt` 加 `task_skill` 参数,与 general_v1「对应 skill 先 load」组合 → 主动 load。否决完整 SKILL.md 预注入。
- **imagegen skill 加 ⛔ 调 tool 前必须贴 prompt + BLOCKING 等确认**:把模型脑内装配摊到对话层让用户最后过一眼防白烧 ¥0.22;诊断五维→六维加比例/尺寸。
- **新增 imagegen skill**:单文件五步法(诊断模糊度→给推断+待确认→拍板→装配 prompt→调 seedream);mermaid vs seedream 选型三段式。
- **登录页加「管理员添加用户」入口 + 删 chat meta 条/tok 显示**:`create_user`(CLI/web 共用)+ `POST /v1/auth/admin/create_user` 校验 `ZCBOT_ADMIN_TOKEN`。否决 User 表加 is_admin 列。
- **新增 documents skill(内部材料学科知识库 document_search API)**:四函数 Bearer 认证,search 返整篇 Markdown,反模式约束只 print 前 300 字防爆上下文;库=7 材料学科英文论文 21W+ 文件 + 跨语言语义检索;与 research(OpenAlex)互补。
- **dev SPA SSE 客户端重连**:`fetchSse` 拆 consume + 重连壳(1/2/4s 退避 ×3);后端 `stream_events` 入口检 run_status 非 running 立即吐 done 关流。
- **research skill fetch_pdf 改走静态直链 + list 端点加直链 + pg_trgm GIN 索引**:绕开 paper_pdf_view 路径 bug;`?search` 30s→几十 ms;SKILL 加「XML 优先 PDF」。
- **顶栏 token 累计修(`sync_task_tokens` 改走 messages SUM)**:切 streaming 后内存计数器永不更新,改现算 + backfill。
- **同 wd 并发软警告 banner + `/v1/tasks` 加 `run_status` 筛选**:Claude Code 同款「信任 + 软警告」;否决硬挡/short_id 全产物隔离/clone task(DESIGN §7.9)。
- **paper_server → research skill**:范式判断走 skill(非 tool/MCP/裸 httpx),`skills/research/{SKILL.md,paper.py}` 三函数。

### 2026-05-20

- **dev SPA artifact chip 演进**:对话内 tool_call/result 挂产物 chip,门控落到产物工具白名单 `ARTIFACT_PRODUCING_TOOLS={seedream,seedance}`,assistant 正文走 seenRels 去重 + allowInlineMedia 防二次 inline。
- **CLAUDE.md 加「实施前先对方案」段**。
- **loop.py tool message 补 `name` 字段 + backfill 历史**:OpenAI tool spec 本有 name,缺它致历史回放无 banner/chip。
- **dev SPA 输入区删上传按钮 + 加「✨ 润色」按钮**:`POST /v1/tasks/{id}/optimize_prompt` 走 task model 装配 LLM,计费 `kind=prompt_optimize` 不污染顶栏。
- **顶栏加生图模型下拉 + 中间产物图片/视频内联展示**:`GET /v1/image_models` 扫 yaml,`build_agent(image_variant=)`;`renderArtifactBarHtml` 走 blob URL inline,切 task 回收 blob。
- **LLM 调用切 streaming(cancel 秒退)+ 发送/停止合并单按钮**:`chat_stream(stream=True, include_usage=True)` + `stream_chunk_builder` 拼回,chunk 间 poll cancel;前端打字机靠 emit text delta。
- **dev SPA seedream tool 透明性 banner**:tool 返串首行 `[seedream] model=...· cost=¥...`,前端正则 parse 挂折叠徽章。
- **豆包 Seedream 5.0 接入 + 0007 cost_usd → cost_cny 全表统一币种**:`tools/seedream.py` 走 ark_client,产物落 `<wd>/figures/`;0007 全表 ×7.2 折 CNY;仅 ARK_API_KEY 设了才挂。
- **`POST /v1/files/delete` 加 `recursive` + 顶层目录 task 引用闸**:recursive 走 rmtree;顶层目录被 task 引用 → 409。
- **fs tool 输出渲染 user_root-relative 路径**:`Tool` 加 `user_root` + `_display(p)`,消 chip 404 + 防 uuid/部署根泄漏。
- **`POST /v1/tasks/{id}/clear` 清空对话**:同事务 lock + 检 running + DELETE messages + reset 三列累计;usage_events 全不动(账单 source of truth)。

### 2026-05-19

- **0006 模型切换(c 模式 task 级 A 粒度)+ usage_events v2 表**:`tasks.model_profile` 变 source-of-truth,顶栏下拉 PATCH 下条 send 生效;message 历史按 `messages.model_profile` 画切换点;usage_events 重建多态形态(units jsonb,chat 已接入)。
- **dev SPA 登录撤回邮箱+密码,删 invites 表**:「邀请码 env→invites 表」一日游撤回;复用 users.email + bcrypt;dev SPA 双 tab 登录。
- **SENTINEL user 彻底撤(数据 + 代码)**:web 必走 JWT 后 sentinel 无角色;CASCADE 删 + 10 处代码删;`build_agent` 加 `*` 让 user_id 必填。
- **任务/文件行 `⋯` 下拉菜单 + tool_result debounce 刷新右侧**:单例浮层菜单(position:fixed)避 pane overflow 裁剪;tool_result debounce 500ms 刷新文件 panel。
- **proposal skill mermaid 强制 + quality_check 加图相关 4 拦截**:模型曾写满 ASCII 字符画;render_diagrams caption 强制必填+唯一,quality_check 加四条;`/v1/files/download` 加 `Cache-Control: no-cache`。
- **dev SPA 文件预览弹框**:点击不再直接下载,90vw 模态按扩展名分派(image/pdf/text/md + docx-preview + SheetJS);vendor 入 git(~1MB)。

### 2026-05-18

- **入口归位:`cli.py`→`main.py`,原 `main.py`→`core/agent_builder.py`,删 CLI REPL,§7 E 撤**:`main.py` 原混三角色按 SoC 拆,CLI 只剩 `db/probe/web/user`;dev SPA 已是 dogfood 主路径,REPL 无双 transport 维护税。
- **0004 schema 大瘦身:删 runs / usage_events 旧版,合 run_status/run_error 入 tasks;路由 run_id→task_id**:单活 run 形态客户端只需 task_id。
- **`POST /v1/files/rename` + 顶层目录 delete 加 task 引用闸**:`/v1/files/*` 升格唯一目录树 mutation 入口,DB-FS 一致性服务端内化;顶层目录走 DB-aware 分支(SELECT FOR UPDATE + 409 + check_no_subtask + UPDATE 先于 FS)。
- **task 级 cancel + AgentLoop 协作式 cancel + dev SPA stop 按钮**:Broker 加 per-task `threading.Event`;Loop 加 cancel_check + `_fill_cancelled_tool_results`;同步 call 本身不可中断(后接 streaming 修)。
- **`POST /v1/tasks/{id}/messages` 单活 run 锁 + 孤儿 reaper**:同事务 SELECT FOR UPDATE + 活跃检查 + 标 running 三步原子;lifespan reaper 清 crash 孤儿。
- **proposal skill 流程图/结构图管线**:`render_diagrams.py` 扫 mermaid → mmdc/mermaid.ink → png;render_docx 识别 `![](...)` + mermaid 围栏;图编号递增。
- **system prompt skill 机制改「可选辅助」**:简单问答/读代码/改 bug 不必硬套 skill;接 GET /v1/skills 下拉。
- **`GET /v1/skills` + dev SPA skill 字段改下拉**;**dev SPA 全套 UI 中文化**(技术字段不动)。

### 2026-05-17

- **0003 schema:name + working_dir + skill 三件套**:任务标识与工作目录解耦;`TRUNCATE tasks CASCADE` + 加 `name NOT NULL`;`GET /v1/folders` 给 datalist。
- **`GET /v1/tasks` 分页 + 多维筛选 + ordering**:`{page,page_size,count,results}` + 6 query;allowlist 防注入;默认 `-created_at`。
- **task 硬删 API + dev SPA delete + 文件 per-row 删**:`DELETE /v1/tasks/{id}` 删 DB 行(messages CASCADE)+ **FS task_dir 不动**(同 name 多 task 共享)。
- **files API 全面 user-rooted + dotfile 过滤**;**files 面板 UX 项目名 + 修 root crumb bug**。
- **task_dir 改 eager mkdir**:`build_agent` + `create_task` 都 `mkdir(exist_ok=True)`,name=项目声明。
- **task = name-based 项目目录 + memory dotfile**:废 UUID 派生 + `tasks/` 中间层;`task_dir = workspace/users/<uid>/<name>/`,同 name 共享;memory 搬 `.memory/`;`validate_task_name` 拒 `.` 起头。

### 2026-05-15

- **§7 D 阶段 `/v1` JSON API 落地;Phase G Jinja2/HTMX UI 路线撤**:删 templates + jinja2/markdown-it-py/pygments;SSE event 由 HTML 片段切 JSON;dev.html 留作 dogfood 主路径。
- **§7 D' 过渡 auth(PLATFORM_KEY → JWT)+ dev SPA**:pyjwt HS256 + `AuthConfig.from_env()` fail-fast;数据隔离全 `Task.user_id == user_id`;SSE 走 fetch + ReadableStream 手解。
- **task_dir 改相对存储**:DB 存 ROOT 内→相对 posix;`core/paths.py::{ROOT,to_db_path,from_db_path}`;alembic 0002 backfill;CLAUDE.md 加「开发期不写兼容层」。
- **workspace 布局统一 per-user** + **litellm 启动 cost map 网络警告兜底**(`LITELLM_LOCAL_MODEL_COST_MAP=True`,冷启 ~5s→<1s)+ **Phase G Jinja2/HTMX Web UI 全撤**(沉淀的 sink/broker/no-subtask/files 安全归一保留)。

### 2026-05-14

- **§7.1 心智模型修正:Folder-centric → Task 一等公民 + Dir 文件副视图**:dir 不是 task 父容器,双视图正交。
- **§7 B Steps 1-4 + 6**:SQLAlchemy 2.x ORM(5 表)+ alembic + `cli db`;`state.json` 全废入 PG;`check_no_subtask` 查前缀嵌套。

### 2026-05-12

- **§7 改写**:platform/core 多租户方案废弃,改 user-direct(folder-centric → task-primary;task/messages 入 PG;no-subtask;hard cascade)。

### 历史(2026-Q1 → 05-11)

- **Phase 1-4**:骨架 / 三 skill / run_python / Model Profile + Probing;ppt v3 商务红 + apply_brand + Iconify;素材摄取改 markitdown CLI。
- **05-06 → 05-11**:Phase 6 部分(task + state.json + tokens 累计);TUI rich + `/resume`;DESIGN §7 初版(05-12 重写);`cli.py export` + `core/export_docx.py`;`atomic_write_text` + `core/memory.py`;loop 事件流化铺 SSE 路。
- **06-04 token 优化启动(DESIGN §8.2)**:chat usage 记 cache hit/miss + LiteLLM cost=0 时按模型档案 CNY/Mtok 兜底;`run_python` 加 `script_path` 模式;`core/context.py` 压缩旧 tool / load_skill / assistant tool_call arguments 不改持久化历史;`llm_start` SSE 输出 `context_*` 压缩统计。
- **06-05 记账给缓存命中折价**:发现 task `tokens_in` 88.6% 是缓存命中却按全价记致虚高 2-3x;`ModelCapabilities` 加 `cache_hit_cny_per_mtoken`,成本公式拆三段;前端 hover tooltip 显真实成本 + 命中率(分母改 usage_events 同源,恒 ≤100%);历史走 backfill 脚本。

---

## 关键决策与偏差

| 项 | 决策 | 备注 |
|---|---|---|
| 工具基目录 | cwd(读)+ working_dir(写) | system prompt 同时注入两者绝对路径 |
| Workspace 布局 | `workspace/users/<user_id>/{.memory/, <name>/}` | per-user 隔离;memory dotfile 防撞;同 name 多 task 共享 |
| Eval Suite | 不做 | 个人工具 dogfooding |
| 版本化 prompt | 直接 `general_v1.md` | 真要切再做 |
| run_python 沙盒 | subprocess + env 过滤 | Docker 在 §7 C 阶段 |
| 兼容层 | 开发期不写 | DB schema / 字段 / API 改动直接切,见 CLAUDE.md |
| `/v1/files/*` 与 DB | files API 作目录树唯一 mutation 入口,DB-FS 一致性服务端内化 | rename / delete 顶层目录 DB-aware |
| 单活 run | task 同时最多 1 个活 run | gate 在 `post_message` 同事务 `SELECT FOR UPDATE` |
| LLM 调用走 streaming | `chat_stream` + `stream_chunk_builder` 拼回;chunk 间 + tool_call 间 poll cancel | cancel 延迟 100ms 级;content delta 即时 emit |
| 上下文压缩 | 加压力门槛,超阈值才压 | 护前缀缓存 + 不丢旧细节(§8.2 / 06-10) |
| 停机判据 | max_iterations 降为 backstop,靠进展信号掐空转 | `_RepeatGuard` + run 级 `_stall`(06-10) |

---

## 文件清单

```
core/capabilities.py        75   ← 模型档案增加 CNY/Mtok 计费兜底字段
core/llm.py                151   ← litellm 离线 cost map env + chat_stream(stream + include_usage)
core/loop.py               300   ← sink.emit + _stream_llm(chunk 间 poll cancel + emit delta)+ _RepeatGuard + usage cache 明细
core/context.py             95   ← LLM 调用前压缩旧 tool / load_skill 消息(带压力门槛),保 tool_call 协议字段
core/sinks.py              101
core/paths.py               50   ← task_dir db form 归一
core/probe.py              243
core/session.py            153   ← ORM
core/task.py                82   ← PG-backed TaskState
core/skills.py             180   ← 多来源 registry(SkillSource)+ source 标记 + 覆盖感知(user wins)+ load_errors + container_dir
core/memory.py              81   ← per-user `.memory/` dotfile
core/export_docx.py        383
core/storage/{__init__,engine,models,usage,utils}.py  ← 4 表(0004-0007 演进);record_chat/image_usage
core/ark_client.py         105   ← 火山方舟 HTTP 客户端
core/agent_builder.py      340   ← 装配 lib(有 ARK_API_KEY 才挂 SeedreamTool);build_skill_registry 装两来源
core/executor.py / sandbox/{network,pool}.py / executor_docker.py  ← Executor ABC + Docker per-user 容器池
tools/{base,fs,shell,run_python,skill_tool,skill_authoring,seedream,seedance,web_search,web_fetch,documents,materials_project}.py  ← skill_authoring=save_skill/fork_skill(host-side 写 user .skills)
main.py                    ~210  ← 入口:web / db / probe / user / sandbox check
db/migrations/versions/    0001-0008
web/app.py                ~1360  ← /v1 JSON API + user_id 隔离 + run lock + cancel + files + pptx 预览 + skills(列表/正文/删)
web/auth.py                ~190  ← 邮箱密码 + platform_key → JWT
web/broker.py / sinks.py / pptx_render.py
web/static/dev.html + js/*.js    ← dev SPA 拆 15 个零构建 ES module(main.js 入口;skills.js=技能查看 modal)
web/static/vendor/        ~1 MB  ← jszip / docx-preview / xlsx
─────────────────────────────────
Python 合计              ~3400 行(+ dev SPA + vendor 1MB);加 skills 脚本 + 配置,总仓库约 3800 行
```

---

## 下一步候选(性价比排序)

1. **CORS 收紧**(~半小时,已接入真实用户应尽快做)—— `allow_origins` 从 `*` 改读 env 域名 allowlist;与 OIDC 解耦。
   - **真 OIDC**(~1 天,选做)—— `/v1/auth/login` 内部换 ID token 校验(路由层 Depends 不动);**邮箱密码长期保留并存**。platform_key 信任模型可接受则可延后,真要弃 PLATFORM_KEY 共享密钥时再做(延后无技术债)。
2. **§7 C Executor + sandbox 收尾**(~3-5 天,按 §7.5 落地清单)—— 剩 Step 4 完整 egress proxy + Step 3b PGID kill 协议 + xfs project quota OS 层硬化。**Stage C DoD** = 6 条落地清单全完成 + 红队回归通过(metadata IP / PG IP block、残留进程清理、跨 user 网络隔离、egress allowlist)。**多用户在线跑代码 hard prereq**。
3. **Phase 6 context 第二步 task summary**(~1 天)—— 旧消息压成一条 summary(区分硬约束/计划/文件路径/关键事实),不直接塞回旧 tool 原文。

> §7 B + D + D' + 单活 run 锁 + cancel + 0004 schema 瘦身 + 入口归位 + Stage C 主体已完工。剩余:真 OIDC → C 收尾 → F(deploy/billing)。§7 E CLI 双模式撤;Phase G Jinja2/HTMX 撤(详见 DESIGN §7.9)。
