DopaDone Vault Format
The DopaDone vault is an Obsidian-compatible directory of markdown files with YAML frontmatter. All user data — tasks, projects, habits, calendar prep, and settings — lives in plain-text files you can read, edit, process with AI, or move anywhere.
Directory Structure
<vault-root>/
└── dopadone/
├── tasks.md # Internal tasks
├── projects.md # Internal projects
├── presets.md # Weight presets, schedules, location rules
├── settings.md # App settings
├── profile.md # User profile (XP, level, stats)
├── habits/
│ ├── definitions.md # Habit definitions
│ └── active.md # Active habit instances
├── calendar/
│ ├── feeds.md # Calendar feed configs
│ ├── rules.md # Event-matching rules + prep steps
│ └── active.md # Prep step completions
The vault location is user-selectable and stored in ~/Library/Application Support/dopadone/vault-path.txt.
Conventions
File Format
Every data file uses YAML frontmatter (parsed by gray-matter):
---
key:
- id: example
name: Example Item
---
Optional markdown body content
IDs
Two ID strategies are used, depending on the collection:
| Strategy | Used By | Format |
|---|---|---|
| Slug (kebab-case) | Projects, Habits, Presets | my-project-name |
| UUID (v4) | Tasks, Habit Instances, Calendar Feeds/Rules/Steps/Completions | 123e4567-e89b-12d3-a456-426614174000 |
Slug generation: Unicode-normalized, lowercased, non-word characters removed, spaces/underscores replaced with hyphens, collapsed, trimmed. Falls back to "untitled" if empty. Uniqueness enforced by appending -2, -3, etc.
Priority Scale
A consistent 0–4 scale shared across tasks, habits, and calendar prep:
| Value | Label | Meaning |
|---|---|---|
| 0 | Paused | Inactive, excluded from task pool |
| 1 | P4 | No priority (default) |
| 2 | P3 | Low |
| 3 | P2 | Medium |
| 4 | P1 | High / Urgent |
Higher values = higher priority = more likely to be selected.
Time Representation
- Dates — ISO 8601:
YYYY-MM-DD(e.g.,2026-03-19) - Timestamps — ISO 8601 with time:
2026-03-19T14:30:45.123Z - Time of day — minutes since midnight (integer).
0= 00:00,540= 09:00,1440= 24:00. Values above 1440 represent the next day (e.g.,1560= 02:00 next day). - Schedule blocks use 15-minute increments (0, 15, 30, 45, …).
Colors
Always stored as hex strings (e.g., "#b8256f"). Todoist color names are converted to hex at the sync boundary.
Sort Order
Collections use integer sort_order or order fields for explicit ordering. Lower values appear first.
Write Mechanics
All vault writes use an atomic temp-file-rename pattern:
- Content is written to a temporary file (
.{random-hex}.tmp) in the same directory - The temp file is atomically renamed to the target path
- The file watcher marks self-writes to avoid reacting to its own changes (500ms debounce window)
File watching uses chokidar with a 200ms stability threshold. Dotfiles and .tmp files are ignored.
File Reference
tasks.md
Internal tasks (not from Todoist or other connectors).
Frontmatter key: tasks
ID type: UUID
| Field | Type | Description |
|---|---|---|
id | string | UUID primary key |
project_id | string | References an internal project slug |
content | string | Task title |
description | string | Task details (empty string if none) |
priority | number | 0–4 (see priority scale; 0 = paused) |
sort_order | number | Integer position within project |
start_date | string | null | ISO date — when task becomes eligible |
deadline_date | string | null | ISO date — deadline |
is_completed | boolean | Completion status |
completed_at | string | null | ISO timestamp when marked done |
labels | string[] | Array of label slugs |
created_at | string | undefined | ISO timestamp (optional) |
depends_on | string[] | Task IDs this depends on (omitted if empty) |
tasks:
- id: 123e4567-e89b-12d3-a456-426614174000
project_id: personal
content: Review Q1 planning
description: Go through quarterly goals
priority: 4
sort_order: 0
start_date: null
deadline_date: "2026-03-31"
is_completed: false
completed_at: null
labels: [urgent, planning]
created_at: "2026-03-15T09:30:00.000Z"
projects.md
Internal projects (Todoist projects are transient and not stored in the vault).
Frontmatter key: projects
ID type: Slug
| Field | Type | Description |
|---|---|---|
id | string | Slug primary key (e.g., personal-tasks) |
name | string | Display name |
color | string | Hex color |
is_sequential | boolean | When true, only the first non-completed task is eligible |
order | number | Sort order |
parent_id | string | null | Parent project ID for hierarchy |
projects:
- id: personal-tasks
name: Personal Tasks
color: "#b8256f"
is_sequential: false
order: 0
parent_id: null
- id: home-renovation
name: Home Renovation
color: "#60a5fa"
is_sequential: true
order: 1
parent_id: null
At runtime, a source: 'internal' field is added (not stored in the file).
presets.md
Named weight sets that shape the task probability pool, with schedule and location triggers.
Frontmatter key: presets (array of preset objects)
Additional key: system_activation_overrides (Record<string, ActivationMode>)
ID type: Slug (presets)
Labels are bare strings — there is no label registry or labels.md file. Labels are auto-discovered from usage across all task sources.
Preset fields
| Field | Type | Description |
|---|---|---|
id | string | Slug primary key |
name | string | Display name |
color | string | Hex color |
sort_order | number | Sort order |
activation_mode | string | undefined | 'always_on', 'always_off', or 'triggers' (default 'always_off' for new presets) |
trigger_mode | string | undefined | 'all' or 'any' (default 'all', only relevant when activation_mode is 'triggers') |
criteria_weights | Record<string, number> | Criterion string to weight multiplier mapping |
default_weight | number | Weight for tasks matching no criteria (default 1) |
schedule_blocks | ScheduleBlock[] | Time windows for trigger-based activation |
location_rule | LocationRule | undefined | Geographic trigger rule |
Criteria keys are bare strings — labels like work, errands, or auto-injected criteria like priority:p1, due:overdue, deadline:today, assignee:me, last-commenter:others.
ScheduleBlock fields
| Field | Type | Description |
|---|---|---|
id | string | UUID |
preset_id | string | Parent preset slug |
days | number[] | Days of week (0 = Monday, 6 = Sunday) |
start_minutes | number | Start time (minutes since midnight, 15-min increments) |
end_minutes | number | End time (can exceed 1440 for overnight) |
LocationRule fields
| Field | Type | Description |
|---|---|---|
lat | number | Latitude |
lng | number | Longitude |
radius_meters | number | Activation radius |
mode | string | 'inside' or 'outside' |
label | string | undefined | Geocoded place name for display |
system_activation_overrides
System presets are defined in code (read-only rules). Only their activation mode can be overridden in the vault. The system_activation_overrides key maps system preset IDs to activation modes.
presets:
- id: work
name: Work
color: "#3b82f6"
sort_order: 0
activation_mode: triggers
trigger_mode: all
criteria_weights:
work: 2
personal: 0.2
"priority:p1": 5
default_weight: 0.5
schedule_blocks:
- id: a1b2c3d4-...
preset_id: work
days: [0, 1, 2, 3, 4]
start_minutes: 540
end_minutes: 1020
location_rule:
lat: 52.2297
lng: 21.0122
radius_meters: 500
mode: inside
label: Office
system_activation_overrides:
"system:defaults": always_on
habits/definitions.md
Recurring habit definitions with scheduling rules.
Frontmatter key: habits
ID type: Slug
| Field | Type | Description |
|---|---|---|
id | string | Slug primary key |
content | string | Habit title |
description | string | Details |
priority | number | 0 = paused, 1–4 = P4 through P1 |
sort_order | number | Sort order |
rrule | string | Recurrence rule (e.g., "FREQ=DAILY") |
time_window_start | number | Minutes since midnight when habit appears (default 540 = 9:00am) |
linger_minutes | number | How long after occurrence to keep showing (default 960 = 16 hours) |
day_window_start | number | null | Restrict to after this time of day |
day_window_end | number | null | Restrict to before this time of day |
created_at | string | ISO timestamp |
labels | string[] | Label slugs |
depends_on | string[] | Habit IDs this depends on (omitted if empty) |
habits:
- id: take-medication
content: Take medication
description: ""
priority: 4
sort_order: 0
rrule: "FREQ=DAILY"
time_window_start: 480
linger_minutes: 240
day_window_start: null
day_window_end: null
created_at: "2026-01-15T10:00:00.000Z"
labels: [health]
- id: lunch
content: Have lunch
description: ""
priority: 3
sort_order: 1
rrule: "FREQ=DAILY"
time_window_start: 720
linger_minutes: 180
day_window_start: null
day_window_end: null
created_at: "2026-01-15T10:00:00.000Z"
labels: []
habits/active.md
Active habit instances — created when a habit occurrence is due, removed when the linger window expires.
Frontmatter key: instances
ID type: UUID
| Field | Type | Description |
|---|---|---|
id | string | UUID primary key |
habit_id | string | Parent habit slug |
occurrence_date | string | ISO date of this occurrence |
note | string | User note |
encounter_count | number | Times this instance has been shown |
created_at | string | ISO timestamp |
instances:
- id: abc12345-...
habit_id: take-medication
occurrence_date: "2026-03-19"
note: ""
encounter_count: 2
created_at: "2026-03-19T08:00:00.000Z"
calendar/feeds.md
ICS calendar feed configurations.
Frontmatter key: feeds
ID type: UUID
| Field | Type | Description |
|---|---|---|
id | string | UUID primary key |
name | string | Display name |
ics_url | string | ICS calendar URL |
color | string | Hex color (default "#60a5fa") |
is_enabled | boolean | Enable/disable feed |
sort_order | number | Sort order |
feeds:
- id: f1e2d3c4-...
name: Work Calendar
ics_url: "https://calendar.example.com/feed.ics"
color: "#60a5fa"
is_enabled: true
sort_order: 0
calendar/rules.md
Rules that match calendar events and define prep steps.
Frontmatter key: rules
ID type: UUID (rules and steps)
Rule fields
| Field | Type | Description |
|---|---|---|
id | string | UUID primary key |
sort_order | number | Sort order (catch-all rules always last) |
match_pattern | string | Pattern to match event summary |
match_type | string | 'contains', 'exact', or 'regex' |
is_catch_all | boolean | Matches all unmatched events |
steps | CalendarRuleStep[] | Prep steps for matched events |
CalendarRuleStep fields
| Field | Type | Description |
|---|---|---|
id | string | UUID |
rule_id | string | Parent rule UUID |
sort_order | number | Sort order |
name | string | Step title (e.g., “Anything to prepare?”) |
hours_before | number | Hours before event to surface this step |
priority | number | 0–4 priority scale |
labels | string[] | Labels applied to generated tasks |
rules:
- id: r1a2b3c4-...
sort_order: 0
match_pattern: "standup"
match_type: contains
is_catch_all: false
steps:
- id: s1a2b3c4-...
rule_id: r1a2b3c4-...
sort_order: 0
name: Review yesterday's progress
hours_before: 2
priority: 3
labels: [work]
- id: r5e6f7g8-...
sort_order: 999
match_pattern: "*"
match_type: contains
is_catch_all: true
steps:
- id: s5e6f7g8-...
rule_id: r5e6f7g8-...
sort_order: 0
name: Anything to prepare?
hours_before: 48
priority: 2
labels: []
- id: s9a0b1c2-...
rule_id: r5e6f7g8-...
sort_order: 1
name: Anything else before this?
hours_before: 2
priority: 2
labels: []
calendar/active.md
Tracks which prep steps have been completed for upcoming events.
Frontmatter key: prep_completions
ID type: UUID
| Field | Type | Description |
|---|---|---|
id | string | UUID primary key |
event_uid | string | Calendar event UID |
event_start | string | ISO timestamp of event start |
prep_step_id | string | Completed step UUID |
completed_at | string | ISO timestamp when marked done |
prep_completions:
- id: c1d2e3f4-...
event_uid: "abc123@google.com"
event_start: "2026-03-20T10:00:00.000Z"
prep_step_id: s1a2b3c4-...
completed_at: "2026-03-19T14:00:00.000Z"
Completions are automatically cleaned up after the event has passed.
settings.md
App-level settings stored as a flat key-value document.
Frontmatter key: top-level keys (no collection wrapper)
| Key | Type | Description |
|---|---|---|
todoist_token | string | Todoist API token |
todoist_user_id | string | Todoist user ID |
sound_enabled | string | 'true' or 'false' |
analytics_enabled | string | 'true' or 'false' (default 'true') |
update_channel | string | 'stable' or 'beta' (default 'stable') |
connectors | object | Per-connector config (nested) |
weight_assignee_match | string | Numeric string (default '2') |
weight_response_bonus | string | Numeric string (default '2') |
collaborator_assignee_weights | string | JSON object: collaborator ID to weight |
collaborator_response_weights | string | JSON object: collaborator ID to weight |
todoist_token: "abc123..."
todoist_user_id: "12345678"
sound_enabled: "true"
analytics_enabled: "true"
Note: values are stored as strings and parsed at read time.
profile.md
User profile with running counters for the XP/leveling system.
Frontmatter key: top-level keys (no collection wrapper)
| Field | Type | Description |
|---|---|---|
total_xp | number | Cumulative experience points (default 0) |
level | number | Current level (default 1) |
tasks_completed | number | Total tasks completed (default 0) |
total_xp: 4250
level: 7
tasks_completed: 142