# zcbot 开发笔记 (给 Claude Code)

## 环境

- **Python 虚拟环境**: `.venv/`(项目根目录下),所有依赖装在里面
- 跑脚本 / 测试一律用 `.venv/Scripts/python.exe ...`,**不要用全局 `python`**(没装 litellm/python-pptx 等会报 ModuleNotFoundError)
- requirements 见 `requirements.txt`

## Shell:别把 PowerShell 语法喂给 Bash 工具

PowerShell here-string `@'...'@` **只在 PowerShell 工具里有效**;用 Bash 工具跑会把首尾的 `@` 当字面量塞进字符串(踩过:commit message 首尾混进 `@` 行,只能 amend + force-push 收拾)。规矩:
- **多行文本(commit message / 文件内容)优先写进临时文件,再用 `-F <file>` / 重定向喂命令**,绕开两种 shell 的引号差异 —— 最稳。
- 必须内联多行时:Bash 工具用 `$'...\n...'` 或 heredoc `<<'EOF'`;PowerShell 工具才用 `@'...'@`。别混。
- 一句话:**先看自己在用哪个工具(Bash vs PowerShell),再选对应语法**。

## 实施前先对方案

非平凡改动(改 >1 个文件 / 涉及行为变化 / 多个候选方案有取舍)动手前**先用自然语言把方案讲给用户确认,认可后再写代码**。一次性 bug 修复 / 改字面量 / 样式微调 / 加日志这类无歧义动作可以直接动手。

讲方案时要包含:
- 问题定位(指到具体文件 / 行号 / 当前行为)
- 至少 1 个替代方案 + 当前方案为什么更优
- 涉及性能 / 兼容 / 数据迁移时主动说

理由:开发期需求漂移快,写到一半被推翻代价高 —— 口头对齐方案是最低成本的纠偏机会。

## 开发阶段心智(公测期:保证对外兼容)

**已进入公测期**(对外真实用户在用,DB 里是真实用户数据 + 线上正在跑的会话)。心智从"开发期可随意 break"切换到**对外面必须向后兼容、对内部实现仍以最优为准**。判断一处改动能不能随意改,先问:**它是不是外部用户能感知 / 依赖的契约?**

**对外契约 —— 必须保证兼容,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、对外冻结行为后再统一清理废弃面。

## 文档维护

每完成一步实现(commit 前),**必须更新 `PROGRESS.md`**:
- "已完成关键能力" 段加一条 `YYYY-MM-DD / <短标题>:<改了什么>`
- 状态表(§7 B Step 几 / Phase 几)若变化跟着改
- 文件清单若新增 / 删除模块跟着改

**每次 commit / push 必须 bump 版本号** —— 单一事实源是 `core/__init__.py` 的 `__version__`(web/app.py 的 FastAPI version、`/healthz` 返回、前端左栏底部展示都引这里,改版本只动这一行):
- patch(`0.8.x`):bug 修复 / 重构 / 调参 / 新加 skill / 样式
- minor(`0.x.0`):成批新功能 / 明显的对外行为变化
- major(`x.0.0`):1.0 正式发版 / 不兼容大重构
- 当前 `0.x` **公测期**,1.0 留给"对外冻结行为 / 正式 GA"那一刻;公测中保持 `0.x` 迭代,minor 走新功能、patch 走修复

**bump 版本时若改动用户可感知 → 顺手在 `CHANGELOG.md` 顶部加条目**(格式 `## <版本> — <日期>` + bullet):口径面向用户("能干什么了"而不是"改了哪个模块"),纯内部重构 / 修复不记——不是每个版本都要有条目。这份由 `GET /v1/changelog` 解析、前端点版本号展示,别写内部模块名 / env 变量 / 部署细节。

**只有以下情况才动 `DESIGN.md`**(避免把工程笔记沉淀成设计):
- 架构 / 心智模型变化(如 §7.1 task-primary 重写)
- 取舍决策推翻或新增(§5 / §7.9 类内容)
- API / schema 字段语义变化(§7.2 / §7.4)
- 实施中发现 DESIGN 描述与代码偏离 → 同步改回

bug 修复、重构、新加 skill、调参 —— **不动 DESIGN**,只更 PROGRESS。

**改任何对外行为(CLI 选项 / REPL 命令 / env 变量 / 文件布局 / migration 步骤)→ 同步更新 `RUN.md`**:
- 新加 / 改 / 删 CLI 子命令 + 选项时,改"日常命令"段
- env 变量 / 启动初始化变化时,改"环境" / "一次性初始化"段
- 真实踩过的坑(用户报或自己跑出来),加一行到"故障兜底"表
- 纯内部重构 / 不影响用户怎么跑的 —— **不动 RUN**

三文档边界:`DESIGN`=为什么(架构 / 取舍),`PROGRESS`=做到哪(状态 / 历史),`RUN`=怎么跑(命令 / env / 兜底)。一次改动可能动多个,但每个动的理由要符合上述边界。

**新增 / 修改 / 删除 skill(`skills/<name>/SKILL.md`)→ 同步更新 `SKILL_LIST.md`**:这份是给外部人看的能力清单(分类 + 一句话 + 何时用 / 不用 + 典型产物 + 核心能力),改 skill 名 / 描述 / frontmatter / 新增删除 skill 都要跟着改,顺手把开头的"最后更新"日期和"Skill 总数"刷新;若新 skill 与已有 skill 有协作模式,补到末尾"跨 skill 协作"段。
