docs: 消解 AGENTS.md 规则冲突,项目契约迁入架构文档

现象:AGENTS.md 里 5 处规则互相打架——逐次询问 vs 认领/提交/升版本;不构建 vs 前端必须
build;全部提交 vs 禁 add -A;每次提交 bump 版本号,而实际 24 次提交版本仍是 0.0.8。
docs/agent_board.md 没有 Agent 列,认领规则无法落地;docs/agent_workflow.md 是 0 字节;
docs/todo.md 与 docs/progress/todo.md 双账本漂移,旧文件还写 platforms/gdgxk.py。

根因:规则按「想防的事」逐条追加,没有回头消解冲突;项目契约和 Agent 行为混在同一个入口文件里。

边界:
- AGENTS.md 只留行为规则;日志 / 目录 / 插件契约指向 docs/architecture.md(两节内容已并入,未丢失)
- 版本号改为只在发版提交时 bump,普通提交不动
- 看板改四列表头,状态带时间戳「进行中 (MM-DD HH:mm)」,超 24h 可接管
- 删除 docs/todo.md;其中 2 条仍未修的代码问题已迁入 docs/progress/todo.md,待办按 待做 / 已知偏差 / 未验证 重排
- 不改代码、不改版本号、不打 tag

运行了什么:git status 自查(只提交这 6 个文档文件,uv.lock 是原有改动未提交);提交时 pre-commit 跑 ruff / web typecheck / commitizen
没跑什么:没跑测试、没构建、没跑 pnpm build(本次无代码改动)
This commit is contained in:
study-tool
2026-11-01 20:04:52 +08:00
parent aa48145557
commit 106818cfc2
6 changed files with 205 additions and 118 deletions
+55 -35
View File
@@ -1,60 +1,80 @@
# AGENTS.md
本文档用于约束本项目中的 AI / 自动化开发行为。开发时优先遵循本文件,其次遵循用户当前消息。
本文档用于约束本项目中的 AI / 自动化开发行为。
**优先级:用户当前消息 > 本文件 > 其他文档。** 与本文件冲突时以用户消息为准,并在收尾时问一句要不要同步改本文件。
## 基本原则
- 先读现有代码,再动手修改,优先沿用项目已有结构和写法。
- 先声明后动手:AI 开工前,必须在 docs/agent_board.md 或任务看板中认领路径。如果发现路径已被其他 Agent 占用,必须停下等待,不能硬改。
- 禁止 git add -A:在共享仓库中,必须按具体文件名暂存,防止误提交其他 Agent 未完成的改动。
- 任何任务,必须在独立的 git worktree 或独立工作区中执行,严禁在 main 工作区直接并行修改。
- 写代码保持最少行数,能简单实现就不要引入复杂抽象。
- 标准格式、协议、解析、压缩、加密、日期等通用能力优先使用成熟稳定的库,不要手写底层实现,除非用户明确要求或项目已有实现必须沿用。
- 不要为了“兼容更多场景”写大量分支,只实现当前明确需要的功能。
- 项目尚未上线,不需要兼容旧数据;表结构或字段调整时直接按新设计修改,不写旧字段兼容、数据迁移兜底或删除旧表的清理逻辑,除非用户明确要求。
- 每次写完代码,不需要检查语法,不需要执行构建,用户会自己做。
- 不要改无关文件,不要顺手重构。
- 任何文件编辑、改动、删除或格式化前,必须先询问用户是否允许;只有得到明确同意后才能修改。
- 不要改无关文件,不要顺手重构;顺手发现的 bug 不修,记进 `docs/progress/todo.md` 并注明位置。
- 如果工作区已有用户改动,不要回滚,不要覆盖;只在必要范围内追加修改。
### 动文件之前(Plan-first)
- 修改**已存在文件**前,先给一次改动清单:路径 / 改什么 / 为什么 / 影响面;同意后本次任务内连续执行,不逐文件问。
- 新建文件、新建目录不用逐次问,但要列进清单。
- 删除、覆盖、格式化、`git reset --hard`、`git checkout .`、`git push -f`、删分支 / 删 worktree,必须单独列出、单独同意。
- 用户当条消息里点名的改动,视为已同意。
### 验证口径
- **产物生成必须做**:前端改动 → `cd web && pnpm build`,`static/` 一起提交,不直接改 `static/` 里的产物。
- **质量验证不做**:不跑测试、不跑全量 lint / typecheck,由用户执行。例外:用户明确要求、发版、或改到核心逻辑(认证 / 计费 / 写库 / 定时任务 / 并发 / 对外协议)。
- 提交时 pre-commit 会跑 `ruff-check` / `ruff-format` / `web typecheck`,失败就修;这不算“检查语法”。
- 结论必须带依据:命令、`文件:行`、报错原文;没核实就写「未核实」。
- 命令失败、文件读不到、解析失败 → 结论是「未知」,不是「空」或「没问题」。
- 没跑的验证写 `Not tried: <原因>`。
## 多 Agent 协作
- 看板表头统一为 `| 路径 | 任务 | Agent | 状态 |`,历史行 `Agent` 列留空。
- 开工前在 `docs/agent_board.md` 追加一行,状态写 `进行中 (MM-DD HH:mm)`(如 `进行中 (11-01 19:40)`)。同一路径已有别人的 `进行中` → 停下问,不硬改。
- 完成后只改自己那行:状态改 `已完成,待实测`,**保留行**(它是完成记录)。不动别人 Agent 的行。
- 超过 24 小时仍是 `进行中` 的行(按状态里的时间算)视为过期,可以接管,但要注明接管人。
- 只有一个 Agent 在顺序开发时,认领行可以省,但改前必须先看有没有人动过同一文件。
- **并行开发(≥2 个任务同时改代码)** 时,每个任务用独立 worktree:
```bash
git worktree add ../study-tool-<任务名> -b <任务名>
cp .env ../study-tool-<任务名>/
uv sync
pnpm --dir web install
```
合并回 master 后 `git worktree remove ../study-tool-<任务名>`。单人顺序开发不强制建 worktree。
- 禁止 `git add -A` / `git add .`:必须按具体文件名暂存,防止误提交其他 Agent 未完成的改动。
## 反复提醒沉淀
- 如果开发过程中总是遇到某个问题,或者用户反复提醒或同一个问题或同一个注意事项被用户纠正2次以上,需要把该注意事项补充到文档`docs/agent_workflow.md`中。
- 同一个坑出现 ≥2 次(或同一个注意事项被用户纠正 2 次以上)才写成规则;只出现 1 次的先记 `docs/agent_workflow.md` 的「观察中」。
- `docs/agent_workflow.md` 每条格式:规则 / 为什么 / 证据(提交 sha、报错原文、`文件:行`)/ Seen N×。
- Seen ≥3 且用户同意,才升级进本文件;写入前先全文搜重,有同义条目就改旧的,不新增重复条目。
- 补充时写成明确、可执行的规则,避免只写模糊描述。
- 关于“遇到重复问题时的总结沉淀流程”及“自我进化机制”,详见 `docs/agent_workflow.md`。
-
## 日志规范
- 所有日志只走 `runtime.log(key, msg, level="info")`:它统一写控制台、内存(`/api/log`)和 SQLite(`/api/log/history`),三处内容必须一致。
- 禁止在新代码里自己 `print()` 写业务日志,也不要直接调 `store.run_log_add()`;模块只负责组织 `msg`。
- key 约定:账号相关用账号 id(页面卡片「课程日志」直接显示);全局 / 系统事件用 `"system"`(右上角「系统日志」弹窗显示)。
- 时间戳、级别、格式都由 `runtime.log` 统一拼(`时间 [级别] 内容`),模块里不要自己加时间前缀。
- 前端读日志统一用 `/api/log?key=&since=`(实时)+ `/api/log/history?key=&tail=`(历史兜底),不要另起一套日志接口。
## 代码结构 / 平台插件
- 业务代码都在 `src/study_tool/` 包里;根目录只放启动壳 `app.py`、`pyproject.toml`、`static/`、`data/`、`script/` 这类运行时资源。通用脚本收进 `script/`,不参与运行的参考脚本放 `script/reference/`;只服务某个平台的脚本(含运行必需的 JS)放该插件目录的 `script/` 下(`plugins/<名字>/script/`),不放根目录,保证删掉插件目录不留残件。插件专属的常量、请求模型、提示词放插件自己的 `config.py` / 模块里,核心目录不写具体平台的内容,也不写死平台 id。
- 平台实现只能放在 `src/study_tool/plugins`:`plugin.json` 只放 `id` / `title` / `name` 注册信息,`main.py` 放 `Platform` 子类,实现复杂的再拆 `jspx.py` / `study.py` 等同目录模块;`load_plugins()` 扫目录 import,新增插件不用改任何 Python 代码(`GET /api/plugins` 看清单,`POST /api/plugins/{name}/enable|disable` 启停,`POST /api/plugins/reload` 热重载)。`src/study_tool/platforms/` 只放运行时(`base` / `contract` / `errors` / `registry`),不再放具体平台。能力只能通过「在子类里实现同名方法」绑定,禁止再写 `Platform(id=..., study=..., session=...)` 这种逐项传函数的注册;模块末尾 `PLATFORM = registry.register(<子类>())`,一个平台只注册一次。同一平台的不同页面代际拆成插件目录内的实现模块,由平台模块按 `course["generation"]` 分发。
- 新增平台的标准步骤:建 `plugins/<名字>/`,写 `plugin.json`(`id` / `title` / `name`),实现 `study(self, key, s, d, **opts)`,需要登录 / 课程列表 / 发现时再实现 `session` / `dump_cookies` / `login` / `list_courses` / `discover` / `renew_session` / `profile` 对应方法,没有的阶段不写,末尾 `PLATFORM = registry.register(X())`)→ 启动即可,不用改 `load_builtin()`、`engine.py`、`routes/`、前端。
- 引擎、路由、前端都不允许直接 import 某个具体平台模块;需要平台能力时用 `registry.get(course["platform"])`;账号级操作(登录 / 刷新课程 / 发现课程 / 会话)按 `accounts.platform` 用 `registry.get()` 取 `Platform` 上的 `session` / `dump_cookies` / `login` / `list_courses` / `discover` / `renew_session` / `profile`;「添加账号」的平台下拉来自 `GET /api/platforms`。
- 前端源码在 `web/`(React + TypeScript + Vite + Tailwind CSS),构建产物输出到 `static/` 并提交进仓库;改完前端必须执行 `cd web && pnpm build`,不要直接改 `static/` 里的产物。
- 新代码的 import 一律显式(如 `from study_tool.config import JSPX`),不要再新增 `from xxx import *`;历史遗留的 star import 只保留在拆分前就存在的模块里。
- `docs/architecture.md` 维护分层与插件契约,改动结构时同步更新。
- Agent 只提议并附证据,**不自己改本文件**。
## 文档规范
- README 保持简洁,只放项目介绍、核心功能、快速开始和文档入口。
- 后续待办写到 `docs/progress/todo.md`。
- 后续待办写到 `docs/progress/todo.md`,分三段:待做 / 已知偏差 / 未验证。
- 已实现但还需要用户测试确认的事项写到 `OPTIMIZATION.md`。
- 每次任务完成前,都要根据实际变更检查并更新 `OPTIMIZATION.md` 和 `OPTIMIZATION.md`;如果功能或待办没有变化,也要确认无需修改。
- 每次任务完成前,根据实际变更检查并更新 `OPTIMIZATION.md` 与 `docs/progress/todo.md`;没有变化也要确认无需修改。
- 文档里的命令、路径、字段名必须与代码一致;引用代码写 `路径 + 符号名`,不写行号。
- 文档不要写过期日期;除非用户明确要求记录具体时间。
## 发版本流程
- 发版本时,先把 `CHANGELOG.md` 的 `Unreleased` 变更整理成新的版本记录,并保留空的 `Unreleased` 标题。
- 每次提交必须将当前版本号进行提升,更新 `metadata.yaml`里面的`version`中的版本号。
- 将当前未提交的代码全部提交到 Git。
- 提交完成后,给当前提交打最新版本号对应的 tag,例如 `v0.0.5`。
- 发版本流程中不要执行编译、测试或构建,除非用户明确要求。
- 如果触发重要的决策,你需要将代码提交到`git`中,并且将本次已经修改的内容进行总结
- **只在发版提交时提升版本号**:`metadata.yaml` 的 `version` 与 `pyproject.toml` 的 `version` 一起改(bumpversion);普通提交不动版本号。
- 提交范围:只提交本次任务的文件(`git add <具体路径>`)。提交前 `git status` 必须只剩本次任务的文件;出现别人的或无关的改动 → 停下问,不顺手提交。
- 提交完成后,给当前提交打最新版本号对应的 tag(如 `v0.0.8`)。打之前 `git tag -l` 查重、`git rev-parse HEAD` 确认,不覆盖已有 tag。
- 发版本流程中不要执行编译、测试或构建,除非用户明确要求;前端改动仍需 `pnpm build` 并把 `static/` 一起提交。
- 完成一个能独立回滚的改动就提交一次(一个 commit 一个目标)。提交信息:标题 `<区域>: <用户可见的变化>`;body 写 现象 → 根因 → 边界(哪些变了、哪些没变)→ 运行了什么 / 没跑什么。
## 项目契约
- 日志约定、目录分层、平台插件契约统一维护在 `docs/architecture.md`,改动结构时同步更新;本文件不再重复这些内容。