Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions docs/migration-v4-to-v5.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,17 @@ Existing tasks without an `id` remain valid and are not changed. Path-based inte

## mdbase collections

If the [mdbase integration](settings/integrations.md#mdbase) is enabled, TaskNotes v5 publishes the portable `tasknotes.task` contract (version 0.3.0-rc.5) used by TaskNotes App and other compatible tools. It is the same contract, byte for byte, that TaskNotes App installs, so the plugin and the app can share a collection. Tasks can list assignees as links to person notes; the property is optional.
If the [mdbase integration](settings/integrations.md#mdbase) is enabled, TaskNotes v5 publishes the portable `tasknotes.task` contract (version 0.3.0-rc.6) used by TaskNotes App and other compatible tools. It is the same contract, byte for byte, that TaskNotes App installs, so the plugin and the app can share a collection. Tasks can list assignees as links to person notes; the property is optional.

### TaskNotes settings record

Collection-wide TaskNotes behaviour is stored in `_settings/dev.tasknotes.md`, a settings record that TaskNotes App reads and writes too. It holds natural-language capture triggers, body templates for new tasks and occurrences, whether archiving moves the file and where to, whether completing a task stops its timer, whether recurring tasks uncheck body checkboxes, the link format written to frontmatter, and per-status next status, cycle exclusion and auto-archive settings. Everything else, such as property mappings, statuses, priorities and filename rules, stays in the task type.

TaskNotes creates the record when it upgrades a collection: from your saved settings for a v4 collection, or from the earlier task type for a 5.0 beta or TaskNotes App collection. This happens in the same backed-up step as the task type upgrade. If the record already exists, TaskNotes keeps every setting it holds and adds only the ones it lacks. A setting missing from the record is read from an earlier task type that still holds it, otherwise from your local settings, so a missing or late-syncing record never loses a value.

Editing the record changes these settings in TaskNotes; changing them in TaskNotes writes only the changed fields to the record. Other fields and the note body are kept. If both changed, TaskNotes asks which to keep. A record written by a newer settings version is used as written and not changed.

If another file in `_settings/` also claims to be the TaskNotes collection settings record (`app: dev.tasknotes` and `scope: collection`), such as a sync conflict copy named `dev.tasknotes (conflicted copy).md`, TaskNotes shows a notice naming those files. It keeps using `_settings/dev.tasknotes.md` and does not change, merge or delete the others. Copy any settings you want to keep into `_settings/dev.tasknotes.md`, then delete the other files.

When the collection's only active type is an unmodified TaskNotes-generated v0.2 type, TaskNotes upgrades the metadata to mdbase v0.3 automatically. Generated formats from 4.3.2 through 4.13.7 are recognized using their historical writers and your saved settings, including BOM/CRLF formatting and reordered YAML mapping keys. Exact historical output containing v4's invalid unquoted status values can also be regenerated safely from those settings. The original bytes of `mdbase.yaml`, the task type and replaced support resources are kept under `.tasknotes/migrations/`; task notes are never rewritten.

Expand All @@ -63,11 +73,11 @@ Incomplete synced metadata is not treated as a new collection. If type or suppor

Additional v0.2 types or genuinely edited task/support definitions are preserved for review. The notice names the affected file. Back up the vault, finish syncing and restart first. If still blocked, restore the unmodified generated type **and matching saved settings** from a known-good backup, or use mdbase to migrate a separate copy and review all type definitions before replacing the active metadata. Do not delete a user-maintained type merely to make ownership recognition succeed. Known generated support resources tolerate line-ending changes; custom commentary or schema edits are not silently overwritten.

If you used a 5.0 beta, TaskNotes updates its task type to the current contract when the vault opens and tells you once. The previous type is backed up under `.tasknotes/migrations/`. The upgrade keeps existing property-role mappings (including custom stable IDs), binding policies, archive tags, occurrence horizons, custom schema constraints, extensions and Markdown body. It lifts the contract version and adds optional assignee support without rebuilding these policies from plugin defaults. Ordinary settings changes update only plugin-owned options. Text, number, date, boolean and list custom properties retain their declarations through migration and later settings saves, including legacy coercion-compatible schemas. An owned property whose type cannot be resolved stops settings import with a named diagnostic instead of disappearing. A leading UTF-8 BOM and CRLF line endings are accepted for canonical metadata as well as historical metadata; recovery copies retain the original bytes. Task files are not changed. Collections that TaskNotes App has already updated retain their task definitions. If a beta added a second task type named `tasknotes-task` after TaskNotes App updated the collection, TaskNotes moves that duplicate into a unique folder under `.tasknotes/migrations/`, preserving its relative path and actual bytes. Explicit record membership keeps the provider in place with a notice naming the type and record. A concurrent edit is preserved and blocks cleanup; task references are never rewritten.
If you used a 5.0 beta, TaskNotes updates its task type to the current contract when the vault opens and tells you once. The previous type is backed up under `.tasknotes/migrations/`. The upgrade keeps existing property-role mappings (including custom stable IDs), binding policies, archive tags, occurrence horizons, custom schema constraints, extensions and Markdown body. It lifts the contract version and adds optional assignee support without rebuilding these policies from plugin defaults. App behaviour settings move from the type to the [settings record](#tasknotes-settings-record) unchanged. Ordinary settings changes update only plugin-owned options. Text, number, date, boolean and list custom properties retain their declarations through migration and later settings saves, including legacy coercion-compatible schemas. An owned property whose type cannot be resolved stops settings import with a named diagnostic instead of disappearing. A leading UTF-8 BOM and CRLF line endings are accepted for canonical metadata as well as historical metadata; recovery copies retain the original bytes. Task files are not changed. Collections that TaskNotes App has already updated retain their task definitions. If a beta added a second task type named `tasknotes-task` after TaskNotes App updated the collection, TaskNotes moves that duplicate into a unique folder under `.tasknotes/migrations/`, preserving its relative path and actual bytes. Explicit record membership keeps the provider in place with a notice naming the type and record. A concurrent edit is preserved and blocks cleanup; task references are never rewritten.

### Interrupted updates and blocked collections

Both v0.2 migrations and beta metadata upgrades are backed up and journaled. Metadata is staged in sibling temporary files and read back. On desktop and mobile, each replacement first moves the actual active revision into a unique recovery path under `.tasknotes/migrations/metadata-swaps/` and compares those moved bytes with the expected original. The verified stage is published only after the active path is empty. When the adapter exposes a vault filesystem path and Node filesystem access, publication uses an atomic no-replace operation even when Obsidian emulates mobile. A late arrival remains active and blocks the update. On non-native storage, automatic migration remains enabled using the adapter's empty-destination move. A verified publication copy is moved while the intended stage, displaced original and journal are retained until active-byte read-back passes. If read-back differs from the intended stage, TaskNotes reports a conflict and keeps that evidence instead of reporting success. A changed revision captured at displacement is retained, restored to the active path if still empty, and reported as a conflict. Native files and containing directories are flushed where supported. The type is updated before activating its new contract, and configuration is committed last. A write failure restores unchanged original metadata. Restarting TaskNotes first recovers interrupted swaps, including an empty active path with its moved original present, then recovers the enclosing transaction before attempting another upgrade.
Both v0.2 migrations and beta metadata upgrades are backed up and journaled. Metadata is staged in sibling temporary files and read back. On desktop and mobile, each replacement first moves the actual active revision into a unique recovery path under `.tasknotes/migrations/metadata-swaps/` and compares those moved bytes with the expected original. The verified stage is published only after the active path is empty. When the adapter exposes a vault filesystem path and Node filesystem access, publication uses an atomic no-replace operation even when Obsidian emulates mobile. A late arrival remains active and blocks the update. On non-native storage, automatic migration remains enabled using the adapter's empty-destination move. A verified publication copy is moved while the intended stage, displaced original and journal are retained until active-byte read-back passes. If read-back differs from the intended stage, TaskNotes reports a conflict and keeps that evidence instead of reporting success. A changed revision captured at displacement is retained, restored to the active path if still empty, and reported as a conflict. Native files and containing directories are flushed where supported. The type is updated before activating its new contract, and configuration is committed last. A write failure restores unchanged original metadata and removes the files the update created, such as new contracts, schemas, the settings type and the settings record, along with its temporary staging files. A created file is removed only while it still holds exactly what TaskNotes wrote; a file edited since is kept. Restarting TaskNotes first recovers interrupted swaps, including an empty active path with its moved original present, then recovers the enclosing transaction, including the same cleanup, before attempting another upgrade.

On iOS/Android adapters that internally check whether the destination exists and then rename, there is still a residual window between those steps. A revision arriving in that window can be overwritten; read-back cannot detect it if the final active bytes equal the intended stage. This adapter path is not an atomic no-replace guarantee. Finish syncing before upgrading, avoid simultaneous metadata updates on other devices, and retain a separate full-vault backup. See [Backup and recovery](guides/backup-recovery.md#interrupted-mdbase-metadata-updates).

Expand Down
11 changes: 11 additions & 0 deletions docs/releases/unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,14 @@ When a change has user-facing documentation, include a canonical tasknotes.dev l
```

-->

## Changed

- With the mdbase integration enabled, collection-wide app behaviour (natural-language triggers, task and occurrence templates, archive moves, stopping timers on completion, resetting checkboxes on recurrence, the frontmatter link format, and per-status next status, cycle exclusion and auto-archive) is now stored in the TaskNotes settings record, `_settings/dev.tasknotes.md`, which TaskNotes App also uses. The task type moves to the `tasknotes.task` 0.3.0-rc.6 contract, which no longer holds these settings. Upgrading a collection creates the record from your saved settings or the earlier task type in the same backed-up step; an existing record keeps its values and gains only missing ones. See [TaskNotes settings record](https://tasknotes.dev/migration-v4-to-v5/#tasknotes-settings-record).

## Fixed

- With the mdbase integration enabled, the first change to a setting stored in the TaskNotes settings record after upgrading a 4.x collection (for example turning off "Move archived tasks to folder" or changing the archive folder) is saved to `_settings/dev.tasknotes.md`. Previously it was kept only until Obsidian restarted.
- With the mdbase integration enabled, TaskNotes shows a notice naming any other `_settings/` file that also claims to be its settings record, such as a sync conflict copy of `_settings/dev.tasknotes.md`. It keeps using `_settings/dev.tasknotes.md` and leaves the other files unchanged. See [TaskNotes settings record](https://tasknotes.dev/migration-v4-to-v5/#tasknotes-settings-record).
- With "Use Markdown links in frontmatter" on, TaskNotes now writes Markdown links even when Obsidian's own "Use [[Wikilinks]]" preference is on. This covers the project picker, dependencies, projects typed in natural-language input (task modal, inline conversion, API, MCP and CLI), the parent-note project default and the Bases current-file default.
- A failed or interrupted mdbase metadata update now removes the files it created (contracts, schemas, the settings type and settings record) and its temporary `.tasknotes-stage-` files, as long as they still hold exactly what TaskNotes wrote. Edited files are kept.
2 changes: 1 addition & 1 deletion docs/settings/integrations.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ These settings control the integration with other plugins and services, such as

## mdbase

Enable the mdbase integration to publish TaskNotes' portable task type, data contract, and schemas for compatible applications. The generated type reflects TaskNotes field mappings, statuses, priorities, filename rules, recurrence behavior, reminders, and user-defined fields.
Enable the mdbase integration to publish TaskNotes' portable task type, data contract, and schemas for compatible applications. The generated type reflects TaskNotes field mappings, statuses, priorities, filename rules, recurrence behavior, reminders, and user-defined fields. App behaviour, such as templates, archive moves, status cycling and natural-language triggers, is kept in the TaskNotes settings record, `_settings/dev.tasknotes.md`, which TaskNotes App shares. See [TaskNotes settings record](../migration-v4-to-v5.md#tasknotes-settings-record).

When TaskNotes v5 opens a collection whose only active mdbase type is an unmodified file generated by TaskNotes v4, it automatically upgrades that metadata from mdbase v0.2 to v0.3. Task notes are not rewritten, and custom collection settings are preserved. TaskNotes keeps the previous `mdbase.yaml` and task type under `.tasknotes/migrations/`, generates a legacy-compatible v0.3 type from the retained plugin settings, and restores the v0.2 files if the upgrade cannot be completed. A durable migration record allows interrupted upgrades to recover on the next startup without overwriting concurrent edits. Collections with additional, modified, or hand-maintained types are left unchanged.

Expand Down
3 changes: 2 additions & 1 deletion i18n.manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"mdbaseSafety.settingsSaved": "59932dd7c85b336da1e70963497097a0f25cc3eb",
"mdbaseSafety.blocked": "3610d6309b9c6bf60a6eaf52fbfab285b29e9a88",
"mdbaseSafety.multiple": "8e9edf0677fbb6a0c5c63cbfdb84229bfcde27c6",
"mdbaseSafety.competingSettings": "a4a3430b0d8cd973be5a34b2592a68b9c3a5a537",
"mdbaseSafety.referenced": "8845207c5dd3bd6ee4a7cb39f6614a58355ba625",
"onboarding.title": "f20c32070dc7d3d6880ac085bd73bf066a0eb5bf",
"onboarding.intro": "39651d1dbcc95a937c9b0c9b702c2d4aa987959b",
Expand Down Expand Up @@ -1535,7 +1536,7 @@
"settings.integrations.mdbaseSpec.header": "3122fe68b2d29d2b5955d17b071ff98a8869ec65",
"settings.integrations.mdbaseSpec.learnMore": "698859ca41e7c55409c85ecc3e624db48e036ee3",
"settings.integrations.mdbaseSpec.enable.name": "dbfee159de77e775f50108e2f21550cd8ee024e8",
"settings.integrations.mdbaseSpec.enable.description": "a660038515638e51bbf1025c8802f704353f86e2",
"settings.integrations.mdbaseSpec.enable.description": "1af5405c6f2ae1aa866e8f299f5eb5b6214b4223",
"settings.integrations.timeFormats.justNow": "a69f482dfa32327d1f3e8bd7d5ebf8aaf5c7e0aa",
"settings.integrations.timeFormats.minutesAgo": "05a31e5617ba90e7dcf1aa3e1124a0d1d40983c7",
"settings.integrations.timeFormats.hoursAgo": "e41f72e387f7a43ddab945f3a6dd0d1ae0b14fa5",
Expand Down
Loading