团队协作

Flow2Spec 的团队协作不是把所有运行状态都塞进 Git。它先把内容分成两类:个人正在做什么,留在本地;已经足以影响团队判断的知识,进入仓库。

对应到目录就是一句话:

  • .task/ 是个人过程,默认不进 Git;
  • .Knowledge/ 是团队知识,和代码一起评审、提交、拉取。

这条边界比具体命令更重要。边界立住以后,developerId、kb-delta.json 和 topic revision 都只是让它可执行的机制。


1. 仓库里有三种协作对象

内容典型路径谁能看到怎么协作
个人任务现场.task/<developerId>/当前开发者和本机 Agent本地续作,不提交
项目知识.Knowledge/拉取了仓库的所有成员delta 合入、Git 评审
实现与正式文档业务代码、docs/拉取了仓库的所有成员常规分支、PR、commit

.task/ 里可以有尚未验证的判断、执行到一半的 checklist、需要用户处理的代办。它们对当前会话有用,却不等于团队事实。把这类文件推给所有人,得到的通常不是透明度,而是一批没人敢删、也没人知道是否过期的运行残留。

.Knowledge/ 正好相反。Agent 会用它判断业务规则、实现边界和读取路径,因此团队成员必须看到同一个版本。知识可以暂时不完整,但不能按开发者各存一份。

大致的数据流如下:

.task/alice/active/...  --生成 delta--\
                                      +--> plan/apply --> .Knowledge/ --> Git
.task/bob/active/...    --生成 delta--/

个人过程(隔离)                         团队事实(共享)

两个人的 delta 不会在 .task/ 里互相合并。每个人只处理自己的任务根,最终通过 .Knowledge/ 的 Git 变更会合。


2. 任务隔离:先确定自己的 TASK_ROOT

多人模式由项目根 flow2spec.config.json 控制:

{
  "collaboration": {
    "enabled": true,
    "developerId": "alice"
  }
}

解析成功后,Alice 的任务根是 .task/alice/,Bob 的任务根是 .task/bob/。rules/f2s-task 只允许 Agent 读写当前 TASK_ROOT;为了找“可续作任务”而遍历其他人的目录,属于越界。

developerId 从哪里来

Flow2Spec 按下面的顺序取第一个可用值:

  1. collaboration.developerId
  2. Git user.email 的 @ 前缀
  3. Git user.name
  4. 都取不到时使用 legacy 单根 .task/

显式配置的 id 会规范化为小写的 [a-z0-9-]。如果配置值无法规范化,例如纯中文或纯符号,解析器会直接报错;这是用户已经明确配置过的字段,不能悄悄换成另一个身份。

从 Git identity 推断时稍有不同。纯中文等值会变成稳定的 dev-xxxxxxxx hash id,并附带 warning。任务仍然隔离,只是目录名不够直观。固定团队最好显式写一个可读 id,排查共享开发机上的路径时省事。

legacy 不是错误模式

以下两种情况会看到旧式路径:

.task/todo.json
.task/active/<task-name>/
.task/completed/<YYYYMMDD>-<task-name>/
  • collaboration.enabled 明确设为 false;
  • 配置和 Git identity 都无法提供 developerId。

legacy 保留是为了兼容单人旧项目。它能正常工作,只是不适合同一工作区多人共用。已经存在的 legacy 任务不会被自动搬进某个开发者目录,因为系统无法判断那些任务原来属于谁;迁移要由人确认后进行。

完整路径规则见 目录与路径约定。


3. 知识合并:用 delta 表达意图

多人改知识库最麻烦的不是 Markdown 能不能合并,而是机器不知道一段 diff 的意图。kb-delta.json 把变更限制成几种明确动作,让合并前可以先做结构校验和版本判断。

一份最小 delta 类似这样:

{
  "taskId": "add-payment-rule",
  "developerId": "alice",
  "baseRevisions": {
    "payment-rules": 3
  },
  "changes": [
    {
      "type": "appendBody",
      "targetTopic": "payment-rules",
      "summary": "补充退款时限",
      "content": "## 退款时限\n\n审核通过后 3 个工作日内原路退回。"
    }
  ]
}

这里真正承担并发判断的是 baseRevisions。它表示 Alice 写这份 change 时,payment-rules 的磁盘版本是 3。flow2spec kb plan 会拿它和当前 .Knowledge/topics/payment-rules.md 的 revision 比较:

  • 仍是 3,可以合入;
  • 已经变成 4,拒绝自动合入,先重读新内容;
  • topic 不存在,或新建 topic 已被占用,同样报告冲突。

修改现有 topic 时,技能约定必须记录对应的 baseRevisions。手写 delta 如果省掉这项,解析器仍能读取文件,但这份 change 也失去了 revision 预检,不应进入团队提交流程。

锁的粒度是 topic,不是某一行或某一节。两个人即使修改同一个 topic 的不同段落,只要其中一份先把 revision 推进了,另一份也要基于新版本重新确认。这个选择偏保守,但不会让两段各自合理、合起来却互相矛盾的业务规则悄悄进入知识库。

四种 change

delta 只接受下面四类动作:

type用途
appendBody在现有 topic 正文末尾追加内容
replaceBody替换现有 topic 正文
updateFrontmatter更新 topic 元数据
createTopic新建 topic;可同时带 matcher 和 task route

字段级 schema、CLI flags 和退出码统一放在 命令说明 § 7,这里不再复制一份规范。

旧知识库第一次启用这套流程时,topic 可能还没有 revision。先执行一次 flow2spec kb build --fix-topics,检查生成的 frontmatter diff,再用 flow2spec kb check --strict 验收并提交;这是一项迁移动作,不是每天都要跑的初始化命令。

delta 的生命周期

一份落盘 delta 通常经历这些状态:

技能形成变更意图
  -> 写入当前 active 任务的 kb-delta.json
  -> plan 预演
  -> apply 写入 topic / matcher / routing
  -> build/check 校验知识图
  -> .Knowledge/ 随代码提交
  -> 任务满足门禁后归档到 completed/

plan 不写文件,适合在任何修改前反复跑。apply 会重新做一次 plan,发现 revision 已变化就停止,不会带着过期结果继续写。

delta 属于任务现场。任务归档后它仍跟着留在本地 completed/ 中,不需要提交,也不应该被当成团队知识的副本。真正的合并结果以 .Knowledge/ diff 为准。


4. 一次正常的协作过程

假设 Alice 要补一个已落地的退款规则。

开工

先拉团队当前版本,再看本地知识状态:

git pull
flow2spec kb status

kb status 检查 routing 漂移、topic 健康度,以及 当前 TASK_ROOT 下 active 任务里的 delta。它看不到 Bob 本机的 .task/,也不应该看到。

工作中

Alice 通过 f2s-kb-feat、f2s-kb-fix、f2s-kb-distill 或 f2s-kb-sync 形成知识变更。技能负责把事实、证据和变更意图整理成 delta;CLI 负责判断这份 delta 还能不能合入当前磁盘版本。

日常使用不要求开发者手动背下五个 kb 子命令。走 f2s-git-commit 时,Agent 会在提交前执行 check + status,唯一定位到当前任务且 delta 可合并时,继续完成 plan -> apply -> build -> check。遇到多个候选或 revision 冲突才停下来让人判断。

需要单独排查、接 CI 或检查某份 delta 时,可以直接使用 shell CLI:

flow2spec kb plan .task/alice/active/add_payment_rule/kb-delta.json
flow2spec kb apply .task/alice/active/add_payment_rule/kb-delta.json
flow2spec kb build
flow2spec kb check --strict

收工

知识已 apply 后,先看 .Knowledge/ 的真实 diff,再和实现一起提交。.task/ 不在 staged files 里。

git diff -- .Knowledge
git status --short

团队拿到的是代码、知识库和提交说明,而不是 Alice 会话里走过的每一个步骤。


5. 两个人改了同一个 topic

revision 是磁盘乐观锁,不是远端服务上的全局锁。它不会隔空知道队友刚刚 push 了什么,所以开工先 pull、提交前保持分支新鲜仍然是协作前提。

假设 Alice 和 Bob 都基于 payment-rules revision: 3 写了 delta:

  1. Alice 先 apply,topic 变为 revision 4,并完成 push。
  2. Bob 的 delta 还留在本地,尚未 apply。他先拉取 Alice 的提交。
  3. Bob 再 plan,得到 revision mismatch 3 -> 4。
  4. Bob 阅读 revision 4 的正文,判断两份规则该并列、改写还是放弃其中一份。
  5. Bob 更新 change 内容和 baseRevisions,再 plan;通过后 apply,topic 进入 revision 5。

第 4 步不能由“把两边文本拼起来”替代。revision 冲突的价值就在这里:它把语义裁决留给掌握业务上下文的人或 Agent,而不是让字符串合并假装成功。

如果 Bob 已经在旧分支 apply,随后 git pull 才出现 <<<<<<<,那是 Git 层冲突。两类冲突不要混用处理方式:

冲突何时出现处理方式
revision 冲突kb plan/apply 阶段拉取最新 topic、重读语义、重写 delta
Git 冲突merge/rebase 后文件出现冲突标记按 Git 冲突处理;知识上下文文件可走 f2s-kb-merge

f2s-kb-merge 不负责替你更新过期的 baseRevisions。


6. 团队最好提前约定的几件事

不需要为 Flow2Spec 单独设计一套复杂流程。下面几条约定已经足够覆盖大多数团队:

  • 每个人配置稳定、可读的 collaboration.developerId,共享机器上尤其如此;
  • 开工先拉取,知识变更和对应实现放在同一个 PR 或一组能互相追溯的提交里;
  • topic 按单一职责拆分。topic 过大不仅影响路由,也会扩大 revision 冲突面;
  • 不直接把 .task/ 加回 Git,不用 todo.json 充当团队看板;
  • topic 正文尽量走 f2s-kb-* 形成 delta。直接手改可以被 Git 记录,却绕过了 revision 预检;
  • 合并前看 .Knowledge/ diff,不只看 kb plan 的“mergeable”。结构可合并不代表业务表述一定正确;
  • 团队统一 Flow2Spec 版本。CLI schema 和模板能力不一致时,协作问题很难靠流程约定补救。

topic 已经大到频繁互相阻塞时,先判断它是否混进了多个独立职责。拆 topic 往往比放宽锁粒度更省维护成本。


7. 管理者怎么看进度

.task/ 不进 Git,manager 自然看不到每个人的 active checklist。这不是缺了一个同步功能,而是所有权边界:尚未验证的个人过程不该自动变成团队记录。

团队进度应从已经形成承诺或产出的地方看:

  • PR / draft PR:正在交付什么、卡在哪里;
  • commit 与 .Knowledge/ diff:能力和规则实际发生了什么变化;
  • .Knowledge/req-docs/:已经确认、等待实现的需求或技术方案;
  • 项目里程碑:跨多个提交的阶段性结果;
  • 团队原有的 issue、项目看板或日报:负责人、截止时间和阻塞项。

如果团队确实需要共享“进行中”状态,优先接已有 issue 或看板,而不是同步 .task/active/。后者包含会话上下文、临时判断和用户代办,权限与生命周期都不同。

有人可能想把 .task/<id>/completed/ 白名单放回 Git。这可以作为团队自定义方案,但不是 Flow2Spec 默认流程。采用前至少要定清楚保留周期、敏感信息清理、谁负责归档,以及它和正式里程碑的关系,否则很快会出现两套进度真值。


8. 常见问题

.task/ 已经是本地目录,为什么还要 developerId?

同一台构建机、devcontainer 或共享工作区可能有多个身份;更重要的是,Agent 需要一个明确的读取边界。TASK_ROOT 让“不要续作别人的任务”从提醒变成可检查的路径约束。

换一台电脑后,原来的 active 任务会自动出现吗?

不会。.task/ 默认不进 Git,它是本机过程状态。跨机器交接应把已确认内容写进 PR、需求文档或 .Knowledge/,不要依赖复制任务目录。

collaboration.enabled: false 会关闭知识库协作吗?

不会。这个开关只决定 .task/ 是否按 developerId 分层。.Knowledge/ 是否共享仍由 Git 和团队分支流程决定。

kb status 能看到队友还没提交的 delta 吗?

不能。它只扫描当前 TASK_ROOT/active/。队友可见的进度应出现在 PR、issue 或其他团队系统里。

revision 和 Git commit hash 是一回事吗?

不是。revision 是单个 topic 的整数版本,用于判断 delta 的读取基线是否过期;commit hash 描述整个仓库的一次提交。前者解决 topic 级预检,后者仍是团队共享和历史追踪的事实源。

kb plan 通过后,为什么 Git 仍可能冲突?

plan 只比较当前磁盘上的知识库。若本地没有拉到远端最新提交,它无法感知远端变化。保持分支新鲜仍是必要条件。

可以直接编辑 .Knowledge/topics/*.md 吗?

技术上可以,Git 也会记录,但这样绕过了 delta 的 schema 与 revision 预检。临时人工修复时必须同步维护 frontmatter 的 revision,再跑 flow2spec kb build 和 flow2spec kb check --strict,并认真检查 routing 是否同步。

apply 成功就可以删除 delta 吗?

不必单独删除。它属于任务目录,任务满足验收与归档门禁后会一起进入本地 completed/。团队只以 apply 后的 .Knowledge/ 为准。


相关文档