--- name: dev-changelog description: >- 三层开发进程记录系统(fragment 源 + 自动生成视图)。Agent 完成代码改动后, 写入一条独立的 fragment 文件并重建视图,提供从一句话概要到完整日志的多级上下文, 帮助 Agent 跨会话保持对项目进展的感知,并支持多人协作下的低冲突合并与作者区分。 当用户提到"开发日志"、"changelog"、"最近改了什么"、"回顾改动"等时触发。 --- # Dev Changelog 跨会话开发进程记录系统。**数据源是一条一文件的 fragment,三层视图由脚本自动生成。** 这套设计同时解决了三个老问题:体量失控、三层手动同步漂移、以及多人协作时 git merge 冲突。 ## 架构总览 ``` .cursor/changelog/ ├─ entries//.md ← 唯一数据源(一条改动 = 一个文件,提交进 git) ├─ changelog-full.md ← 视图(L1 完整,全部条目) \ ├─ changelog-recent.md ← 视图(L2 摘要,最近 ~20 条) | 全部由 ├─ changelog-headlines.md ← 视图(L3 一句话,最近 ~50 条) | 生成器产出 ├─ changelog-by-author.md ← 视图(按作者聚合) / └─ tools/ ├─ changelog_build.py ← 生成器:entries/ → 4 个视图(确定性、stdlib-only) ├─ migrate_changelog.py ← 一次性迁移脚本(已执行,留作参考) ├─ install-git-hooks.ps1/.sh ← 安装 pre-commit / post-merge git hook └─ git-hooks/{pre-commit,post-merge} ``` | 层级 | 视图文件 | 信息密度 | 窗口 | 注入方式 | |------|---------|---------|------|---------| | L1 完整版 | `changelog-full.md` | 高(完整正文) | 全部 | 用户手动唤醒 | | L2 中期版 | `changelog-recent.md` | 中(3-5 行摘要) | ~20 条 | 检测到关联时自动读取 | | L3 概要版 | `changelog-headlines.md` | 低(1 行) | ~50 条 | 每次会话自动注入 | | 作者视图 | `changelog-by-author.md` | 索引 | 全部 | 按需 | > **重要**:视图文件顶部都标了"由 changelog_build.py 自动生成,请勿手改"。 > 一切修改都改 fragment,再跑生成器。视图改了不算数(下次生成会被覆盖)。 ## 锚点 ID 机制 格式 `CL-YYYYMMDD-HHMM-`(如 `CL-20260715-1521-alice`)。同一分钟多条时在 `HHMM` 后追加字母(`CL-...-1521a-sq`)。历史迁移条目无作者后缀(`author: legacy`),保持原 ID 不变 以维持 pitfalls / deferred / source_chat 等处的交叉引用有效。 锚点 ID 在 fragment 与三层视图中一致,用于逐级 Grep 精准定位(L3 → L2 → L1)。 ## 作者标识(多人协作核心) 每条 fragment 记录作者,体现在三处:① 目录 `entries//`、② 文件名 / ID 后缀、 ③ frontmatter `author` 字段。视图层 L3 行前缀 `@`、L2/L1 有 `author` 字段、 另有按作者聚合的 `changelog-by-author.md`。 **作者 handle 的确定顺序**(写入时): 1. 读 `.cursor/local-env.json` 的 `changelog-author` 字段(设备绑定、gitignored,每位开发者各自配一次,最稳) 2. 缺失则取 `git config user.name`,转小写、取首个英文单词或姓名缩写作为 handle 3. 仍无法确定则用 `unknown` > 建议每位协作者在本机 `.cursor/local-env.json` 里加 `"changelog-author": "<你的handle>"`, > 保证 handle 稳定(不随 git user.name 大小写/格式变化而漂移)。 ## fragment 文件格式 ```markdown --- id: CL-20260715-1521-alice date: 2026-07-15 15:21 author: alice author_name: Alice type: fix merge_source: HEAD status: active superseded_by: title: 一句话标题(用于视图的 H3 标题) source_chat: [对话简述](chat-uuid) tags: - tag1 - tag2 affected_files: - scripts/player/player_controller.gd - scenes/player/player.tscn --- 一句话描述改动内容(≤80 字,进 headlines) 做了什么 + 为什么(3-5 行摘要,进 recent;当本条进入最近 20 时被展示) - **what**: ... - **why**: ... - **decisions**: ... - **notes**: ... (symptom / root-cause / fix / tests / followups / relation 等按需,正文原样进 full) ``` 字段说明: - `type`:`feat` / `fix` / `refactor` / `docs` / `chore` / `infra`(机读,便于统计) - `merge_source`:本条记录时所在分支(`HEAD` / `master` / 分支名),留空则视图不显示 - `status`:`active`(默认)/ `reverted` / `superseded`;后两者配 `superseded_by: CL-xxx` - `affected_files`:**必须全路径**(视图 L2 的硬匹配靠它,缩写路径会失效) - `tags` / `affected_files` 为块列表;其余为标量。frontmatter 之外的三段用 `` 分隔 ## 操作 A:记录写入(核心流程) ### 触发条件 Agent 完成涉及**代码或配置文件实质性改动**的任务后自动触发。 **不触发**:纯对话/方案/答疑(无文件改动)、只读操作、只改 `.cursor/` 下基础设施文件 (画像、延期、changelog 自身、hooks、tools 等)。 ### 写入流程 ``` 1. 确定作者 handle(见上「作者标识」三步顺序) 2. 生成锚点 ID:CL-YYYYMMDD-HHMM- - 检查 entries/ 下是否已存在同 ID(含其他作者目录),冲突则 HHMM 后加字母后缀 3. 从刚完成的任务提取:what / why / where(affected_files, 全路径) / decisions / notes, 并判定 type、merge_source(当前分支)、tags 4. 用 Write 工具创建单个 fragment 文件: .cursor/changelog/entries//.md - 按上面的格式填好 frontmatter + L3/L2/L1 三段 - L3 ≤80 字一句话;L2 3-5 行;L1 完整正文 5. 跑生成器重建视图: python .cursor/changelog/tools/changelog_build.py (Windows 控制台先 set PYTHONIOENCODING=utf-8) 6. 在回复末尾附 "[已记录到开发日志]" ``` ### 与旧流程的关键区别 - **不再用 StrReplace 往三个文件顶部插入**——只新建一个 fragment 文件,零冲突、零半损坏风险 (旧的 StrReplace 顶部插入曾导致条目半损坏,fragment 模型下该风险彻底消失)。 - **不再手动维护滚动窗口**——recent/headlines 的 ~20/~50 截断由生成器自动算。 - **不再手动保持三层同步**——三层都是同一 fragment 的不同密度渲染,天然一致。 ### 静默写入原则 - 不需要用户确认(Agent 对自己的改动是一手认知)。 - 回滚也要记录(新建一条 `type: chore` / 在被回滚条目里置 `status: reverted` + `superseded_by`)。 ## 操作 B:L2 自动触发读取 由 `changelog-recall.mdc` 调度,读取 `changelog-recent.md`: - 硬匹配:当前任务涉及的文件出现在条目 `affected_files`(现为全路径) - 软匹配:当前任务语义关键词与条目 `tags` 有交集 任一命中即纳入上下文。读取的是**视图**,无需读 fragment。 ## 操作 C:L1 手动检索 用户要求回顾完整记录时,读取 `changelog-full.md` 并按时间 / 模块 / tags 过滤展示。 ## 逐级上溯机制 L3 信息不足时(语义模糊),按 ID 逐级 Grep: ``` 1. 从 L3 提取锚点 ID 2. Grep changelog-recent.md(L2)→ 命中读该条 3. 仍不足 → Grep changelog-full.md(L1)拿行号,Read 该行 ±20 行 (也可直接 Read 对应 fragment:entries/*/.md) ``` 原则:按需触发、精准读取、一次 1-3 条、不全文读 L1。 ## 操作 D:记录管理 - **修改**:编辑对应 `entries/*/.md` 后跑生成器(不要直接改视图)。 - **删除**:删除对应 fragment 文件后跑生成器。 - **标记取代/回滚**:在 fragment 里置 `status: superseded`/`reverted` + `superseded_by`。 ## 滚动窗口(自动) 生成器按 ID 时间倒序排序后:headlines 取前 50、recent 取前 20、full 全量、by-author 全量。 **无需任何手动修剪**,被移出窗口的条目仍完整保留在 fragment 与 full 视图中。 ## git 协作与冲突 - **fragment 几乎不冲突**:不同人写不同文件、不同作者写不同子目录。 - **视图冲突自动吸收**:`.gitattributes` 给 4 个视图设 `merge=union`(merge 不中断), pre-commit / post-merge git hook 跑生成器从 fragment **确定性重建**视图,所有人收敛到一致。 - 新克隆需跑一次 `tools/install-git-hooks.ps1`(或 `.sh`)装 hook。 - CI 可加 `changelog_build.py --check` 校验"视图 == 重建(fragment)"防止有人没装 hook 就提交。 - **合并尚未 fragment 化的旧分支**(如重构前的 master):先 `git merge --no-commit --no-ff <分支>` (union 让视图不冲突、`--no-commit` 防止 hook 过早重建),再跑 `tools/merge-import.ps1 [-Ref <分支>]` (或 `.sh`)把对方旧格式条目收割成 fragment(只补缺失 ID、不覆盖已有),自动重建并校验无丢失,最后 `git add -A; git commit`。 - **两条改动撞了同一个 fragment ID**(生成器遇重复 ID 会直接报错):给其中一条改 ID(`HHMM` 后加字母,如 `1521`→`1521a`), 文件名与 frontmatter `id` 同步改,再跑生成器。两条都保留、不丢内容。 ## 与其他系统协作 | 系统 | 关系 | |------|------| | `changelog-recall` Rule | 下游消费者:注入 L3、按需读 L2、上溯 L1 | | `deferred-decisions` | 互补:记"没做什么" | | `profile-memory` | 结构对称:记"是什么" | | `epee-orchestrator` | 在 registry.md 注册本 Skill | ## 自迭代日志 ### 已知必要检查 1. **大任务收尾不要遗漏写入** — 单次 5+ 文件改动时,先按 `changelog-recall.mdc` 的 "任务完成 Checklist" 写完 fragment + 跑生成器,再输出回复;绝不"先回复再补写"。 2. **affected_files 必须全路径** — 视图 L2 的硬匹配依赖全路径;缩写路径(如只写文件名) 会让 recall 的硬匹配失效。迁移前的旧 L2 缩写路径问题已由本次重构(从 frontmatter 全路径渲染)解决。 3. **写完务必跑生成器** — 只建 fragment 不跑 `changelog_build.py`,视图就不会更新(recall 读的是视图)。 pre-commit hook 会兜底重建,但当前会话内的 recall 注入读的是已落盘视图,故应即时生成。 4. **作者 handle 要稳定** — 优先用 `.cursor/local-env.json` 的 `changelog-author`,避免每次从 `git config user.name` 推导导致 handle 漂移(同一人出现多个目录)。 5. **Windows 控制台编码** — 跑脚本前 `set PYTHONIOENCODING=utf-8`(或 PowerShell `$env:PYTHONIOENCODING="utf-8"`), 否则中文输出在 GBK 控制台显示乱码(不影响 UTF-8 文件写入,仅影响 stdout 观感)。