Everyone who manages tasks in Obsidian eventually hits the same wall: a task is a line of text, and edit one character — add one tag — and it "becomes a different task". The sync layer breaks, dashboard counts drift, history vanishes. The root cause is always the data model. In August 2026 I tore down the task system I'd run for two years and rewrote it as Task Vault, now published in the Obsidian Community directory. This post explains why the data model is designed the way it is, and 4 decisions you only make after stepping on the landmines.
The Root Problem: Task Identity = a Hash of the Text Line
In the old system (obsidian-tasks plugin + text-line protocol), a task looked like this:
- [ ] Fix neutralizeFence regex tolerance #hermes 📅 2026-08-20
Over time the sync layer accumulated 10 fixed bugs: key-generation cascades of false completions, double mirrors double-archiving, UTC ordering misjudgments… at least 4 trace back to the same root — the task's identity is a hash of the entire line. Fix a typo in the title and, as far as the syncer is concerned, you deleted one task and created another. The Apple Reminders mirror can't match by hash, so it creates a duplicate; the archive script misses by date-line match, so tasks never get archived.
Structured fields had nowhere to live: priority, status, source, and project ownership were all crammed into inline tags. Queries ran on regexes; changing the format meant changing the key.
Task Vault's Answer: Data Model First
Task Vault's core is not a feature list — it's one decision: one Markdown file per task, all structure in frontmatter, identity is a UUID.
---
id: 76aaff14-99e2-43d6-af5c-46fa13d51af9 # identity, never changes after creation
title: Fix neutralizeFence regex tolerance
status: doing # inbox|todo|doing|waiting|blocked|done|cancelled
due: 2026-08-20T22:00
priority: high
project: "[[magicedit]]"
---
## 任务描述 (Task description)
Background, approach, referenced material.
## 执行记录 (Execution log)
- 2026-08-20 09:40 · **todo→doing** · `user`
Picking up: reproduce first, then fix the regex.
Rename the title, move the date, add tags — the id doesn't move, so every index, mirror, and statistic stays stable. The file is still plain Markdown; uninstall the plugin and your data survives.
4 Non-Obvious Engineering Decisions
Decision 1: The execution log is block-laid-out, not inline-concatenated
Each entry is two lines: a metadata line (time · kind · actor) plus a two-space-indented body line, with blank lines between entries. This is not an aesthetic choice — an early version appended long body text after the metadata, producing 200+ character lines that were unscannable during review. The block layout keeps the time column scannable and the prose readable. And the - YYYY-MM-DD HH:MM prefix is load-bearing: the backstop dispatch cron uses it to detect entry boundaries, so the timestamp format is locked — no alternative notations allowed.
Records are append-only, never overwritten. Each carries a kind (decision/comment/blocker) and an actor (user/hermes/cc/codex) — so a week later, when you ask "why did we choose this", the answer is in the log, not buried in chat history.
Decision 2: A seven-state machine that only offers legal transitions
inbox → todo → doing → waiting/blocked → done/cancelled. Seven states instead of "open/done" buys you the real workflow: waiting (blocked on something external), blocked (dependency incomplete — purely derived, never hand-set), cancelled (distinct from done, preserving the decision trail). The status menu only shows legal transitions from the current state — todo can't jump straight to cancelled, which prevents accidental illegal states. The checkbox complete gesture walks the legal todo→doing→done chain and lands exactly one migration record.
Decision 3: Delegation to AI agents is first-class
In the detail popover: pick an agent (recommended order CC > Codex > Hermes) + write the instruction → the plugin writes the full instruction into the ## 委派 section, sets assignee + dispatched in frontmatter, and fires your dispatch hook (a shell command you configure that launches the matching CLI agent). When the agent picks it up, that single file carries the complete context: delegation instruction + execution-log timeline + task description.
One field-tested lesson is baked into the design: an early version wrote frontmatter without verifying that the hook actually launched an agent — so every "delegation" between 16:26 and 17:20 silently failed. Frontmatter written, agent never started, user thought it was handled. Now the delegate button's return value must distinguish fired / disabled / error, and failures must surface in the UI.
Decision 4: Default DDL = today 22:00, never tomorrow
A behavioral observation: tasks created without an explicit deadline were pushed to "tomorrow" 83% of the time — then pushed again every day until they expired. Task Vault's DDL rule is counterintuitive on purpose: no explicit deadline → today at 22:00. Either finish it today or explicitly reschedule (which lands in the execution log). Silent drift is eliminated.
How It Differs from obsidian-tasks / TaskNotes
| Dimension | obsidian-tasks | TaskNotes | Task Vault |
|---|---|---|---|
| Task identity | Text line | frontmatter + id | frontmatter + UUID |
| Status | Two states (checked/open) | Multi-state | Seven-state machine + legal-transition constraint |
| Decision record | None | Note body | Structured execution log (kind + actor + append-only) |
| AI agents | None | None | Delegation section + dispatch hook + backstop re-dispatch |
| Task shape | Inline text | One file per task | One file per task, plain-Markdown portable |
| Runtime deps | — | Dataview | Zero (258 tests, TS strict mode) |
My call: if you just tick off shopping lists, obsidian-tasks is fine. But if you manage engineering tasks that span days, people, and agents — and need a decision trail — the text-line model won't hold. That's the reason Task Vault exists. TaskNotes is the closest competitor, but its execution log has no actor system, and the AI delegation path is missing entirely.
Its Ecological Niche: One Piece of a Multi-Agent Workflow
Task Vault isn't an isolated plugin in my setup. Six cron jobs (morning study injection, GitHub watch, nightly review…) write task files directly under the protocol; an Apple Reminders mirror syncs bidirectionally every 5 minutes (Siri voice capture flows back as inbox tasks); a terminal-state hook fires notifications on completion. The agent-side write protocol and concurrency guards (dedup rules for multiple crons writing the same directory between 01:00–06:00) are documented in the repo's spec.
The essence of this system: task management becomes the shared memory of a multi-agent workflow — humans, Hermes, CC, and Codex all read and write the same Markdown, with stable identity, tamper-evident records, and a state machine that makes "cancelled then resurrected" dirty states impossible. One of the core problems in agent context engineering is durable, shared state — Task Vault is my answer for the task domain.
Conclusion
Task Vault is live in the Obsidian Community directory (community.obsidian.md/plugins/task-vault), MIT open source. The core claim in one sentence: the reliability ceiling of a task system is set by its data model, not its feature list. UUID identity, a seven-state machine, an append-only execution log — these three decisions make "renaming a title breaks sync" categorically impossible.
If you take one thing from this post: the next time you design any system with "state + sync", first ask what the identity is, where it lives, and what happens when things around it change. Answer those three well and 90% of the pitfalls never exist.
References
- Task Vault repository — github.com/guancyxx/task-vault (published Aug 2026, v0.2.2)
- Obsidian Community directory entry — community.obsidian.md/plugins/task-vault
- Task system protocol spec — docs/任务系统规范.md
- obsidian-tasks plugin — github.com/obsidian-tasks-group/obsidian-tasks
- TaskNotes plugin — github.com/caronchen/obsidian-tasknotes
- On this site: Agent Skills Ecosystem Observations