workflow_version_evolution
道-方法 · 道层 skill 全文
本页是 <code>rules/skills/workflow_version_evolution.md</code> 的逐字投影(仅隐私清洗,零改写)。
时点提示:本页是仓内文件 rules/skills/workflow_version_evolution.md 的逐字投影(仅做隐私清洗:仓库根绝对路径→相对路径、家目录→~/;除此零改写)。若源文件后续有修订,以仓内真源为准。
Workflow: 版本演进 SOP
元数据
- 类型:Workflow
- 范围:版本号决定 + 一句话叙事,覆盖代码 / 设计 / 调研 / skill / schema 五类对外承诺。不包含 changelog 文件结构、依赖管理、hook 实现、ADR。
- 与
workflow_tool_skill_evolution分工(2026-07-07 补,USER-CLAR-EW):本 SOP 只回答"这次改动打不打版本号、叫什么、reason 怎么写"这一个问题,不管一次工具/skill 更新该走哪些步骤、Beta 状态怎么晋级、trace 怎么驱动优化——那是workflow_tool_skill_evolution(rules/skills/drafts/,含 Beta 模式与 maturity 状态机)的职责,其 U6 步骤内部反过来调用本 SOP 判版本号(见该 skill §方法论 A U6)。一次演进无论停在 Beta 还是升到正式版,后续优化都统一走workflow_tool_skill_evolution的动态 workflow,而不是另开一套版本流程;本 SOP 在其中只负责"打不打版本号"这一步的判定,不重复其余流程。 - 核心理念:清晰的判定方法 > 可复现的判定结果。同一情况两次判得略有不同是可接受的,前提是都按本 SOP 的语言描述。
- 演进:v1(2026-04-26 初版,机器可执行风格,含 JSON schema/投票/needs_review,回放后被用户判定为过拟合可复现性)→ v2(2026-04-26 重写,5 类文档 + 一个核心问题)→ v2-patched(同日,§3 加 50 字解决调研型/Schema 例外/纯讨论边界)→ 本版(2026-04-27 加 5 条 patch:Hyrum's Law、CLAUDE.md/cron/hook 点名、非 git 载体脚注、独立 version-manager sub-agent、mask 思想)。
1. 这份 SOP 干什么
任何「对外承诺」(被读 / 被引用 / 被脚本依赖 / 必读约束)发生变化时,本 SOP 告诉你:
- 这次变化值不值一个新版本号
- 如果值,是 X 级还是 Y 级
- 版本号叫什么、reason 写什么、tag 打哪里
事件触发,agent 直接判定。不投票,不喂 JSON 模板,不列穷举清单。
2. 核心判据(只有一个问题)
「这次改完,下游有没有人或脚本必须跟着改自己?」
- 必须改 → X:调用方、读者、脚本被迫调整才能继续工作。例:删已发布字段、强制约束新增、配置默认值反转、设计结论被推翻、INDEX 重排让读者必须重建心智。
- 不必改但接口/约束扩了 → Y:新增能力但向后兼容。例:加可选字段、新增 skill 步骤、补充 boundary 条目、新增章节但不改既有引用。
- 都没有 → 不打版:实现重构、typo、cosmetic、目录搬移、内部细节。
「大改」「小改」不是行数。1 行 YAML 反转可能是 X,500 行重排版可能不打。判的是下游负担。
「接口」不只是显式 API、CLI 参数、字段。长期可观察行为(INDEX 顺序、文件路径、reason 措辞惯例、目录结构)也算接口 — 下游已无声依赖,破坏即 X。
3. 事件分类(什么时候触发判定)
事件来自三种情境:
| 情境 | 现场表现 | 默认动作 |
|---|---|---|
| 调研写入 | 在同一份调研/设计文档持续追加内容 | 中间过程不定版;一轮调研闭环(结论收敛 / 决定继续 / 决定放弃)时定一版 |
| 新建文件 | 创建一份新文件 | 看是否被外部读/引用/校验 — 是则按 Y 处理。Schema 类例外:约束新生产方 = Y;强制既有消费方重新解析 = X |
| 修改已发布文件 | 改一份已被引用/必读的文件 | 默认进入版本判定,按 §2 判 X/Y/不打。修改 = prompt 明确要求改/增/删;纯讨论 / 质疑 / 征询不算修改 → 不触发 |
「调研型 prompt」(请你调研 / 思考 / 提案 / 评估)本身不触发版本号。等真的产出落地、形成可被引用的产物时再定版。强主张原则但没要求当下写入已发布文件 → 不触发,等下一个真正写入的 prompt 来时再判。
4. 文档类型 × 接口定义(查表速记)
每类对外承诺的「接口」是什么、X/Y 各对应什么:
| 类型 | 「接口」是什么 | X 级(必须改) | Y 级(兼容扩展) |
|---|---|---|---|
| 代码(脚本/CLI/库) | 函数签名 / CLI 参数 / 配置 key / 错误码 / 副作用 | 删参数、错误码反转、可逆变不可逆 | 加可选参数、新导出函数、扩错误码 |
| 设计 / 规则文档 | 核心结论 / 边界假设 / 强制约束(MUST) | 推翻原结论、约束语气从 SHOULD 变 MUST、删硬规则段 | 新增约束不推翻原结论、补充论证 |
| 调研文档 | 当前轮的最终结论 | 新证据推翻原结论 | 补充证据但结论不变 |
| Skill 文档 | 触发条件 / 步骤序列 / 输出格式 / 验收标准 | 触发条件反转、必选步骤变可选/反之、删输出字段 | 新增可选步骤、扩触发场景、补充验收维度 |
| Schema(YAML/JSON/SQL DDL) | 字段、字段值代表的策略、约束(required/enum) | 删字段、字段值反转策略、required 加严、列删/改名 | 加可选字段、扩 enum、新增表 |
INDEX / 路由文件按「设计 / 规则文档」处理。重排导航 = X,追加条目 = Y。
「设计/规则文档」典型样例包括 CLAUDE.md / AGENTS.md 的强制要求段、cron 表(config/CRONTAB.md)、Claude Code hook 配置(settings.json 的 hooks 段)、以及任何被 LLM 启动时必读的「配置即承诺」类文件。它们的接口是「读者必读的强约束 + 调度时机/参数」 —— 改一句话整体行为反转。形式上不一定是 Markdown,YAML / JSON 配置只要承担「LLM 必读」职责就归这一类。
记不清类型时,回到 §2 那一句话即可。
5. 现场(事件刚发生)
写入前(手上有 prompt 和计划,还没动 commit):
判一次预判 — 是 X 还是 Y 还是不打。预判记在 task 笔记或 todo 里。落地后回头核对:实际 diff 与预判一致吗?不一致就以 diff 为准。
写入后未 tag(commit 已落或 stop hook 已自动 commit):
版本号管理由独立 sub-agent 负责。主 agent 协调多个 sub-agent 完成任务时,最后派一个 version-manager sub-agent(用 Opus)按本 SOP 统一处理:
- 扫描本轮所有内容性 sub-agent 的产出(文件改动、commit message、changelog 里可能写入的版本号字样)
- 按本 SOP 事件分类 + 文档类型 + 等级判据校核每处
- 与本 SOP 不符就改(覆盖其他 sub-agent 的误改)
- 决定本轮是否打 git tag、tag 名、reason 三段
放最后是为了让 version-manager 看到全貌;放中间会被后续 sub-agent 的改动绕过。单 agent 任务则主 agent 自己按 SOP 判定,不必派 version-manager。
按 §2 判一次。Y 级 agent 直接 git tag -a 0.Y -m "<reason>"。X 级也由 agent 自行判定并写清 reason;如果当前载体无法安全打 tag(例如文件未 commit、非 git 载体未落定、或工作区状态不支持),就在对应 VERSION_LOG / 调研报告 / todo 中记录 tag 建议和阻塞原因,等载体可用时由自动化或后续 agent 继续处理。
commit_subject 匹配 ^rescue:|^cleanup:.recovery|^chore.accumulated → 内容已在 session ref 里,合并到 main 不算新发布,不打版。
6. 后补(扫历史时)
后补不是上帝视角。看 git 历史 / session 记录给每个事件反推版本号时,按那个时间点的可见信息判 — 结合文件创建/修改时间 + 当时的 prompt + 当时的 diff + 当时其他文件状态判,把后续事件 mask 掉。不要用「我知道这方案后来真的实施了」反推「当时就该判 minor」。每个事件落到当时的「时间切片」里看,跨切片的因果不影响切片内的判定。这样保证时间一致性,避免用后果倒推动机。
发现一段历史完全没打版本号、要批量回填时走这套流程。
第一步:识别大改。按 §2 / §4 扫每个 commit 找 X 候选。这些是叙事骨架,每个独立打一个 tag。
第二步:合并近邻小改。剩下的小修补按下面规则聚类:
- 按主题聚类:同一目录 / 同一 skill / 同一设计主线下的连续小改归为一个 Y。例:
AGENT_GUIDE.md在 3 天内被修了 5 次(每次 +5/-3 的细化),合一个 Y,reason 写「连续 5 次细化,主题:sandbox merge 章节扩充」。 - 按时间窗就近:跨主题但发生在两个大改之间的零散 commit,归到下一个大改的 Y 里作为「附带清理」。
- rescue / chore / typo / format 不论数量 → 合并到周边 Y 或集体不打。
- 不打的就不打:纯 typo / 目录搬移 / cosmetic 即使数量多也可以不打。后补不是为打而打。
第三步:模糊时按低位归类。拿不准是 X 还是 Y → 先按 Y 打,reason 里点一句「可能为 X,按 Y 先归档并写明不确定点」。后补阶段重要的是叙事连贯,不是单点精确。
后补的输入可以是 commit subject + per-file numstat、用户当时 prompt 原文、changelog 笔记。任一够用即可,不强求格式。diff 行数用 per-file numstat(git show --numstat),不用 commit-total(避免无关文件被合算 — 实证教训:曾把 git_safety 2 行误判成 942 行)。
7. 等级与命名
等级:只有 X 和 Y 两级。无 patch、无 rc/beta、无模块子版本号。ZeroVer 期 X 永远 0,Y 单调递增。X-bump 事件由 reason 第一句承担语义(不真把 X 从 0 改 1)。脱离 ZeroVer 由人显式宣布 1.0,条件是本仓库之外的人或系统开始依赖。
命名(默认):仓库级一条主版本号线,载体是 git annotated tag。git tag -a 0.Y -m "<reason>"。
非 git 载体:SQL DDL 文件、独立 Markdown 库、跨工具配置目录、纯文档项目等可把「git annotated tag」替换为载体原生承载方式(CHANGELOG 行 / 文件名后缀 _v0.7.md / schema 注释 / commit footer 等)。形式可换,等级语义(X/Y/不打)和 reason 三段写法不变。
命名(例外):
- 调研文档独立计数,tag 加后缀
-research-<topic>(如0.7-research-llm-determinism)。一份调研一个 tag,过程稿不进版本号。 - 某个 skill / 工具被外部独立引用、单独发布(如 weclaude/ 这种嵌套 repo),按它自己的命名规则单独成版。
跨项目一致:X.Y 两级 + git annotated tag + reason 三段式 + 调研后缀约定,所有 zlxbjtu 个人项目都用这一套。换项目不换形式。
8. reason 三段写法
固定顺序,下游一眼能判:
<下游必须做什么>。<改了什么(接口层面)>。<为什么改>。- 总长 ≤200 字
- 不必做改动也写出来(如「调用方无需改动」),让读者一秒确认安全
- 「改了什么」用 §4 该类的接口语言(代码用函数签名 / 设计文档用核心结论 / schema 用字段)
- 「为什么改」可引用触发它的 prompt / backtest / 事故 / 实验结论
major 示例:
调 git 的自动化脚本要审视是否遵守"父 repo 不操作子 repo .git"。git_safety.md §4 新增「嵌套 git repo 边界」段,列出已知嵌套 repo 与边界契约。weclaude/ 嵌套 repo 的 .git 被父 repo 误识为 gitlink,破坏 stop hook 提交流程。
minor 示例:
调用方无需改动。tools/git/AGENT_GUIDE.md 新增 sandbox merge 章节,描述合并流程但不改既有约定。给新成员降低上手门槛。
9. 不写什么(负面清单)
为避免过度工程化,明确不做以下事:
- 不让 agent 投票或多次跑求一致 — 事件触发单次判,结果差异可接受
- 不强制 input/output JSON schema — 自然语言判定,事件→等级→reason
- 不列穷举的 boundary 文件清单 — 用 §2 那句话当场判
- 不写 needs_review 通道 — 模糊就在 reason 里写不确定,由后续 agent 依据新证据继续修正
- 不做 patch、rc/beta、模块子版本号 —
X.Y够用 - 不绑死任何具体仓库语境 — 五类文档同套规则
- 不用行数判大小 — 看下游负担
- 不为打而打 — 整段时间没事件就整段不打
10. 自检(打 tag 前过一遍)
- 这次变化触发了哪种事件(调研写入闭环 / 新建对外文件 / 修改已发布文件)?
- 涉及的文档落在 §4 哪一类,它的「接口」是什么,这次动了接口的哪部分?
- 下游必须改自己吗 → X / Y / 不打?
- reason 三段是否齐全?「下游必须做什么」是不是写在第一句?
- tag 名是仓库级
0.Y还是调研后缀0.Y-research-<topic>?
5 个全 yes 就 git tag -a 走起。任一不确定 → 按低位归类(Y)+ reason 注明不确定点 + todo 加一条后续 agent 审查项。
11. EvolutionRecord 指针(2026-07-02 S6)
tools/evolution_workflow/record.py 的 EvolutionRecord.version_decision 只记录本 SOP 对该更新的 X / Y / none 结果、路径和理由;它不改变本 SOP 的判定语义,也不替代打 tag。若任务明确禁止 commit/tag/stage/push,仍可在 record 中写入 level: Y 或 level: none 作为审计证据,但实际 tag 操作必须留待允许写 git metadata 的后续流程。