diff --git a/CLAUDE.md b/CLAUDE.md index ab52aa9..38574e1 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -24,16 +24,24 @@ PowerShell here-string `@'...'@` **只在 PowerShell 工具里有效**;用 Bash 理由:开发期需求漂移快,写到一半被推翻代价高 —— 口头对齐方案是最低成本的纠偏机会。 -## 开发阶段心智 +## 开发阶段心智(公测期:保证对外兼容) -当前处于**开发测试期**(开发自用 + 内部测试,DB 已有真实测试数据)。改需求 / 重构时,**以最优实现为准,不为旧数据 / 旧字段 / 旧 API 留兼容层**,但**不删现有数据**: -- DB schema 变 → 直接改 model + 写一条干净的 migration:加列 / 改列结构 OK;**不要 truncate / DELETE FROM 现有表 —— 测试数据要保留** -- 删字段(DROP COLUMN)前:若该列是当前唯一持有该信息(如累计型 tokens 列),先 backfill 到新位置再删;若纯冗余(从其他列能推出)直接删 OK -- 字段语义变 → 全量替换 + migration 把旧值映射到新值(不留 `legacy_xxx` / `*_v2` 并存) -- CLI / REPL 选项变 → 直接改,不留 deprecated 别名 -- 只有当用户明确说"这条要保留兼容"时才写兼容代码 +**已进入公测期**(对外真实用户在用,DB 里是真实用户数据 + 线上正在跑的会话)。心智从"开发期可随意 break"切换到**对外面必须向后兼容、对内部实现仍以最优为准**。判断一处改动能不能随意改,先问:**它是不是外部用户能感知 / 依赖的契约?** -理由:兼容层是技术债;但测试数据是观察新代码行为的依据 —— 一次 truncate 后再回去查"上周那 task 烧了多少 token / 哪条消息触发的 bug",就只能瞎猜。 +**对外契约 —— 必须保证兼容,break 前先有迁移路径**: +- **用户数据**:绝不 truncate / DELETE FROM / 重置现有表 —— 这是用户的东西,丢了无法恢复 +- **DB schema**:加列 / 改列 OK,但要写干净 migration 且**平滑兼容线上存量数据**;删字段(DROP COLUMN)前先 backfill 到新位置,确认无引用再删 +- **字段语义变**:全量替换 + migration 把旧值映射到新值,且要考虑**线上正在跑的旧请求**读到该字段时不崩 +- **对外 API(HTTP 接口 / 请求·响应 schema)**:不改既有字段语义、不删字段、不改 URL;要变先加新字段 / 新端点,旧的留一个废弃窗口 +- **CLI / REPL 选项、env 变量、文件布局**:改名 / 删除前保留 deprecated 别名一个版本,并在 RUN.md 标注废弃;直接 break 会打断正在用的人 + +**对内部实现 —— 仍以最优为准,放手重构**: +- 纯内部模块 / 函数 / 私有数据流(外部不可见、无人依赖)→ 该重写重写,不留 `legacy_xxx` / `*_v2` 并存 +- 内部重构只要**对外行为不变**(同样的输入 → 同样的输出 / 同样的 schema),不算破坏兼容 + +**拿不准是"对外契约"还是"内部实现"时 → 当成对外契约处理(先对方案,见上一节)。** 只有用户明确说"这条可以 break / 不用兼容"才走破坏式改法。 + +理由:公测后"随意 break"的前提(只有自己的测试数据、坏了重来)已不成立 —— 现在每次破坏式改动都可能弄丢真实用户数据或打断线上请求。兼容层确实是技术债,但比起搞坏用户数据,这点债值得背;等正式打 1.0、对外冻结行为后再统一清理废弃面。 ## 文档维护 @@ -46,7 +54,7 @@ PowerShell here-string `@'...'@` **只在 PowerShell 工具里有效**;用 Bash - patch(`0.8.x`):bug 修复 / 重构 / 调参 / 新加 skill / 样式 - minor(`0.x.0`):成批新功能 / 明显的对外行为变化 - major(`x.0.0`):1.0 正式发版 / 不兼容大重构 -- 当前 `0.x` 开发期,未正式发版前不进 1.0 +- 当前 `0.x` **公测期**,1.0 留给"对外冻结行为 / 正式 GA"那一刻;公测中保持 `0.x` 迭代,minor 走新功能、patch 走修复 **只有以下情况才动 `DESIGN.md`**(避免把工程笔记沉淀成设计): - 架构 / 心智模型变化(如 §7.1 task-primary 重写) diff --git a/PROGRESS.md b/PROGRESS.md index 2628001..13fe627 100644 --- a/PROGRESS.md +++ b/PROGRESS.md @@ -2,7 +2,7 @@ > 配合 `DESIGN.md`。本文件只记 phase 状态、决策偏差、文件量、下一步。每条 1-2 句:做了啥 + 关键判断;细节查 `git log` / `git diff` / `DESIGN §7.9`。 -最后更新:2026-06-12(admin 管理后台 + 目录/筛选排序/分页/导出 PDF:users.role + require_admin + /v1/admin/* + 独立 admin.html) +最后更新:2026-06-12(进入公测期:CLAUDE.md「开发阶段心智」翻新为"对外契约必须向后兼容,仅内部实现仍可最优重写") --- @@ -21,6 +21,10 @@ ## 已完成关键能力 +### 2026-06-12 / 进入公测期:对外兼容策略 + +- 项目进入公测(对外真实用户在用)。`CLAUDE.md`「开发阶段心智」从"开发期可随意 break、不写兼容层"翻新为**对外契约(用户数据 / DB schema / 对外 API / CLI·env·文件布局)必须向后兼容,仅纯内部实现仍以最优为准放手重构**;拿不准 → 当对外契约处理。版本号段同步:公测保持 `0.x`,1.0 留给"对外冻结行为 / 正式 GA"。同条记忆 `feedback_dev_phase_no_compat` 一并翻新。bump 0.11.0 → 0.11.1。 + ### 2026-06-12(傍晚)修上下文压缩投毒 → run_python 空转报错 - **根因(DB 实测,60 个 task 命中 83 次 `[Error] bad arguments to run_python: code or script_path must be provided`)**:`core/context.py` 把旧 assistant `tool_call.arguments`(>800 字符)压成 `{"_compacted":true,"original_chars":N,"note":...}` marker 发给 LLM。模型在长 doc/ppt 任务里看到几十次"过去的 run_python 长这样",就**照葫芦画瓢把 marker 当真实参数原样吐出来** → executor 拿不到 code/script_path → 报错空转。83 次里 **61 次是模型仿写 marker**(铁证:抓到 `{"_compacted":true,"original_chars":85}`——85<800 压缩器根本不会出手、且缺 `note` 字段,压缩器必带 → 只能是模型伪造),22 次是真·空 `{}`。这正是代码里早已为 `task_progress` 单独豁免、注释明写"会毒化模型"的同一个坑,只是 run_python 没豁免。 diff --git a/core/__init__.py b/core/__init__.py index 4284e3e..33d8464 100644 --- a/core/__init__.py +++ b/core/__init__.py @@ -1,3 +1,3 @@ # zcbot 版本号单一事实源:web/app.py 的 FastAPI version、/healthz 返回、前端展示都引这里。 # 改版本只动这一行。 -__version__ = "0.11.0" +__version__ = "0.11.1"