返回博客
官小西

Task Vault:我给 Obsidian 写了一个把任务当文件管理的插件

用 Obsidian 管任务的人迟早会撞上同一堵墙:任务是一行文本,改一个字、补一个标签,它就「变成另一个任务」。同步器炸了、面板统计错位、历史记录丢失——根因全在数据模型。2026 年 8 月,我把用了两年的任务系统推翻重写成 Task Vault 并发布到了 Obsidian 社区目录,这篇文章讲清它的数据模型为什么这样设计,以及 4 个只有踩过坑才会做出的决策。

问题的根源:任务身份 = 文本行的 hash

旧体系(obsidian-tasks 插件 + 文本行协议)里,一个任务长这样:

- [ ] 修 neutralizeFence 正则容错 #hermes 📅 2026-08-20

这套约定跑久了,同步器积攒了 10 个已修复的坑:key 换代连环误勾、双镜像双归档、UTC 时序判定错乱……至少 4 个坑的根因是同一个——任务的身份是整行文本的 hash。你改标题里的错别字,对同步器来说就是「删了一个任务、新建了一个任务」。Apple Reminders 镜像按 hash 匹配不上,就重复建一份;归档脚本按日期行匹配不上,就漏归档。

结构化字段更是无处安放:优先级、状态、来源、项目归属全塞在行内标签里,查询靠正则,改格式 = 换 key。

Task Vault 的答案:数据模型先行

Task Vault 的核心不是功能,是一个决定:每任务一个 Markdown 文件,frontmatter 承载全部结构,身份是 UUID

---
id: 76aaff14-99e2-43d6-af5c-46fa13d51af9   # 身份,创建后永不改
title: 修 neutralizeFence 正则容错
status: doing       # inbox|todo|doing|waiting|blocked|done|cancelled
due: 2026-08-20T22:00
priority: high
project: "[[magicedit]]"
---

## 任务描述
背景、方案、引用原文都在这里。

## 执行记录
- 2026-08-20 09:40 · **todo→doing** · `user`
  接单:先复现,再改正则。

标题随便改、日期随便挪、标签随便加——id 不动,一切索引、镜像、统计都稳定。文件仍是纯 Markdown,插件卸载数据还在。

数据模型对比

4 个非显而易见的工程决策

决策一:执行记录是「块排版」而不是行内拼接

每条记录两行:元信息行(时间 · 类型 · actor)+ 缩进两格的正文行,条目间空行。这不是审美选择——早期版本把长正文接在元信息后面,一行拉到 200+ 字符,复核时根本没法扫读。块排版让时间列可扫、正文可读,而且 - YYYY-MM-DD HH:MM 前缀是承重结构:兜底派发 cron 靠它识别条目边界,所以时间格式被锁死,禁止改成别的写法。

记录只追加、永不覆盖。每条带类型(决策/评论/卡点)和 actor(user/hermes/cc/codex)——一周后你回看「当时为什么这么选」,答案在执行记录里,不用翻聊天记录。

决策二:七态状态机 + 只显示合法转移

inbox → todo → doing → waiting/blocked → done/cancelled,七态比「未完成/完成」两态多出的是真实工作流:waiting(等外部)、blocked(依赖未完成,纯推导不手设)、cancelled(与 done 区分,保留决策痕迹)。UI 上状态菜单只显示当前状态的合法转移——todo 不能直接跳 cancelled,避免误触产生非法状态。勾选框的完成动作会自动走 todo→doing→done 合法链并只落一条迁移记录。

决策三:委派给 AI Agent 是一等公民

详情弹窗里选 agent(推荐序 CC > Codex > Hermes)+ 写指令 → 插件把指令全文写进 ## 委派 区、frontmatter 落 assignee + dispatched、fire 派发 hook(你自己配置的 shell 命令,按 assignee 拉起对应 CLI agent)。Agent 接手时读这一个文件就拿到全部上下文:委派指令 + 执行记录时间线 + 任务描述。

一个实测教训写进了设计:早期版本派发 hook 只写 frontmatter 不验证 hook 是否真的拉起了 agent,结果 16:26–17:20 之间的每次「委派」都静默失败——frontmatter 写了、agent 没起、用户以为交出去了。现在委派按钮的返回值必须区分 fired / disabled / error 三态,失败必须在 UI 上说出来。

决策四:默认 DDL = 当天 22:00,绝不推到明天

行为观察发现:没有明确截止时间的任务,83% 被创建者默认推到了「明天」——然后每天重复推迟直到过期。Task Vault 的 DDL 铁律是反直觉的:无明确截止 → 当天 22:00。任务要么今天做完、要么显式改期(改期会落执行记录),消灭「无声漂移」。

和 obsidian-tasks / TaskNotes 的差别

维度 obsidian-tasks TaskNotes Task Vault
任务身份 文本行 frontmatter + id frontmatter + UUID
状态 两态(勾/未勾) 多态 七态状态机 + 合法转移约束
决策记录 笔记正文 结构化执行记录(类型 + actor + 只追加)
AI Agent 委派区 + dispatch hook + 兜底补派
任务形态 行内文本 每任务一文件 每任务一文件,纯 Markdown 可迁移
运行时依赖 - Dataview 零(258 项测试,TS 严格模式)

我的判断:如果你只是想在购物清单上打勾,obsidian-tasks 够用;但如果你管理的是跨天、跨人(跨 agent)、需要留决策痕迹的工程任务,文本行模型撑不住——这正是 Task Vault 存在的理由。TaskNotes 是最接近的竞品,但它的执行记录不带 actor 体系,AI 委派链路也是缺失的。

七态状态机

生态位:它是多 Agent 工作流的一环

Task Vault 在我的体系里不是孤立插件。6 个 cron(早报学习注入、GitHub 巡查、夜间复盘……)按协议直接写任务文件;Apple Reminders 镜像 */5 分钟双向同步(Siri 语音录入自动回流成 inbox 任务);终态 hook 在任务完成时推送通知。Agent 侧的写入协议、并发防护(01:00–06:00 多 cron 写同一目录的查重规则)都在仓库的规范文档里。

这套东西的本质,是把「任务管理」变成了 多 Agent 协作的共享内存:人、Hermes、CC、Codex 读写同一份 Markdown,身份稳定、记录不可篡改、状态机保证不会出现「已取消又复活」的脏状态。Agent 上下文工程的关键课题之一就是状态的持久化与共享——Task Vault 是我在任务域的答案。

结论

Task Vault 已上架 Obsidian 社区目录(community.obsidian.md/plugins/task-vault),MIT 开源。核心主张一句话:任务管理系统的可靠性上限,由它的数据模型决定,而不是功能列表。UUID 身份、七态状态机、只追加执行记录——这三个决定让「改个标题就炸同步」这类事故从根上不可能发生。

如果这篇文章只能记住一件事:下次设计任何「状态 + 同步」的系统时,先问身份是什么、在哪、改了会怎样——这三个问题答好了,后面 90% 的坑都不存在。

参考资料

  1. Task Vault 仓库 — github.com/guancyxx/task-vault(2026-08 发布,v0.2.2)
  2. Obsidian 社区目录条目 — community.obsidian.md/plugins/task-vault
  3. 任务系统协议规范 — docs/任务系统规范.md
  4. obsidian-tasks 插件 — github.com/obsidian-tasks-group/obsidian-tasks
  5. TaskNotes 插件 — github.com/caronchen/obsidian-tasknotes
  6. 本站相关:Agent Skills 生态观察