Repository navigation
[FR]: Optional Morgen-compatible completed property, without disturbing existing TaskNotes configuration #2312
Description
[FR]: Optional Morgen-compatible completed property, without disturbing existing TaskNotes configuration
Follow-up to #1098, opened at callumalpass's kind invitation ("A new FR would be great, if you'd be happy to explain in more detail what you're looking for"). cc @0xSnib, who raised the original request and wrote a helper plugin for it.
Code references below are pinned to d686847.
Summary
Let TaskNotes optionally write a boolean completed property — and, at most, two other derived keys — under the names Morgen expects, so TaskNotes tasks can be imported and completed from Morgen without changing anything about how TaskNotes is configured.
Strictly opt-in, off by default, and purely additive: existing property keys and values stay untouched. Enable it, use Morgen for three months, disable it — and the setup is exactly as it was.
TaskNotes stays authoritative on existence: Morgen cannot create a task in the vault, only read and schedule what already exists. It does write back to notes, though — completion state, and (optionally, if you enable it) due_date. So any property this FR adds has to be treated as potentially two-way, not as a one-way derived write.
What Morgen is, and the two ways it reads Obsidian
Morgen is a desktop+mobile calendar and planner that pulls multiple calendar accounts (Google, Outlook, iCloud, CalDAV) and task sources (Todoist, Linear, ClickUp, Google Tasks, Apple Reminders and others) into one view, and lets you drag tasks onto the calendar to time-block them. It reads the vault directly from disk — Obsidian doesn't need to be running.
It can read Obsidian tasks in two quite different ways:
- Inline tasks — individual
- [ ]lines written in the Obsidian Tasks plugin's emoji format, plus a unique🆔per task which Morgen writes into your markdown. (Morgen parses the markdown off disk rather than calling the plugin, but lists it as a requirement — it's what makes that format practical to author, and what interprets the completion markers Morgen writes back.) - Notes as tasks — a whole note becomes one task, driven entirely by frontmatter. No Tasks plugin, no IDs: the file itself is the identity.
The second path is what makes TaskNotes integration natural, since a TaskNotes task already is a note with frontmatter. Everything below concerns that path only.
The two tools are complementary rather than competing. TaskNotes can't schedule the same task into a calendar twice — you reschedule, and it moves — whereas Morgen splits one task across multiple independently-sized work sessions, alongside every other calendar and task source you use. TaskNotes owns the task and its metadata in the vault; Morgen owns the time-blocking. Only the handshake is missing.
Why the #1098 answer isn't enough
Renaming the status property to completed and defining exactly two statuses true/false does produce booleans, but it collapses the status model to two values. Lost: waiting, paused, in-progress, aborted; the colored status dots in inline links, task cards and Bases views; the distinction between done and aborted; status cycling; Kanban columns; per-status auto-archive.
That visual encoding isn't cosmetic — a lot of real work happens through Bases and properties, and the status colors are what make those views readable at a glance.
What Morgen requires
Verified against a current Morgen build (September 2026); the feature is labelled Beta, so the contract may still move.
Import notes as tasks
Notes are converted to tasks if they have the propertycompletedand are not excluded. Markdown tasks inside note-tasks become subtasks.
You can add the following properties (case insensitive) to notes:
| Property | Type | Expected values | Required? |
|---|---|---|---|
completed |
Boolean | true / false |
Yes — this is the gate |
tags |
Tags | Any tag (e.g. #work) |
No |
priority |
Text | highest, high, medium, low, lowest |
No |
due_date |
Date | Panel says YYYY-MM-DD, but date+time is accepted too |
No |
duration |
Text or number | e.g. 60, 30m, 1.5h, 1h30m |
No |
Plus morgen-tasks-include (checkbox) for per-note inclusion.
completed is not merely supported — its presence is the gate. A note without it is not a task to Morgen at all. That's why "rename status to completed" is currently the only thing that works, and why it costs so much.
Two things worth noting:
- Notes-as-tasks needs neither the Tasks plugin nor task IDs. Morgen's unique-
🆔requirement applies to inline markdown tasks; a note-task is identified by its file. So this path avoids all of that machinery. - Morgen names a note-task from its filename, ignoring any
titleproperty (verified). TaskNotes' defaultstoreTitleInFilename: truemeans this works out of the box. Users who instead keep the title in frontmatter and use Zettelkasten, timestamp or UUID filenames would see opaque names like202609101423in Morgen — nothing TaskNotes can fix from its side, but worth knowing, and worth raising with Morgen separately. - There is no upstream to defer to. The official
morgen-so/morgen-obsidianplugin only hides task IDs in the editor — its entire settings interface isdecorateIDs: 'show' | 'hide' | 'replace_with_emoji'. All vault parsing happens in the Morgen desktop app, reading the vault directory directly.
How it maps to TaskNotes
| Morgen wants | TaskNotes | Verdict |
|---|---|---|
completed (required) |
— | New key. The whole ask. |
due_date (date only) |
due or scheduled |
New key, and two-way — see below. |
duration |
timeEstimate (number, minutes) |
New key. Morgen accepts a bare number, so it's a 1:1 copy. |
tags |
native Obsidian tags | Already works. |
morgen-tasks-include |
— | New key. Optional. |
priority |
priority |
Same key, but no code needed. Users set their priority values to Morgen's five and keep whatever labels they like. Sorting is unaffected if views use formula.priorityWeight, which the generated bases already provide; only sorting on the raw property needs the 1-urgent prefix convention from the docs. A docs note, not a feature. |
Three possible solutions
1 — Documentation only. A docs section covering the priority-value guidance below, the completed-as-status workaround from #1098 with its trade-offs stated plainly, and a link to @0xSnib's morgen-taskNotes-sync. An hour's work; doesn't address the gate property. That plugin re-derives TaskNotes' internal state from outside by reading its settings at runtime, and its author writes: "I fully expect the functionality to be wrapped into either morgen-obsidian or Task Notes."
2 — Bidirectional completed ↔ status. (my preference) One new optional field mapping for a boolean key, plus a designated status to link it to — configured much like the existing per-status auto-archive option. Outbound: write completed: true|false from the status's existing isCompleted flag. Inbound: an external flip moves the task to the designated completed or open status.
Small in scope, because the machinery exists: StatusConfig.isCompleted, the user-configurable completedDate mapping, updateCompletedDateFrontmatter() writing a derived field purely from isCompletedStatus(), and getCompletionTransition() already driving webhooks, calendar sync and auto-archive. The outbound half is essentially a second completedDate that writes a boolean instead of a date.
Priority stays a documented manual step here — the user renames their own priority values, as described below. Combined with that, tags and priority both work, so this alone gives a usable Morgen setup with only the deadline and estimate missing.
3 — Full property set. Solution 2 plus derived due_date, duration, optional morgen-tasks-include, and automating the priority step that Solution 2 leaves to the user.
Priority values
Morgen expects five fixed values; TaskNotes allows any number. Since this approach uses TaskNotes' own priority key rather than a second derived one, the fix is simply to name the priority values after Morgen's vocabulary. Labels are separate and stay free, so nothing changes visually in TaskNotes.
Map by rank, not by name: order the configured priorities by weight and distribute them across Morgen's scale, centred on medium. TaskNotes' own defaults give high → high, normal → medium, low → low, with highest and lowest available to anyone running five levels.
Using fewer than five appears safe. As far as I can tell Morgen offers no way to change an Obsidian task's priority, so nothing should come back that TaskNotes doesn't recognise, and a three-priority setup simply shows three priorities. I'd flag this as worth confirming rather than assuming — Morgen turned out to write due_date back when I had thought completion was the only inbound property.
Five is the ceiling. Because each priority needs a distinct value, a sixth would have to be a string Morgen doesn't recognise — and unrecognised values fall back to "default priority" (verified). More than five levels is out of scope; the setting and docs should just say Morgen supports at most five.
A genuinely unset priority — TaskNotes' none — is the one case for writing no key at all. Note that Morgen renders any unrecognised or missing priority as "default priority", so omission must not be used to express "normal": that would signal "unreadable" where the user meant something specific.
Under Solution 3 this becomes a toggle, and it cannot be settings-only. Changing a priority's value leaves every existing task file carrying the old string, which then matches no configured priority — so the toggle has to rewrite files too, with a preview of how many are affected and a way back. For a fresh vault that's free; for an established one it's a migration.
Dates: due or scheduled?
TaskNotes separates due (the commitment deadline) from scheduled (when you intend to do the work) — its docs put it exactly that way. Morgen has only due_date, and it means a deadline. Scheduling in Morgen happens as real events in a connected calendar — Morgen has no calendar of its own ("you will need to connect at least one external calendar account"), so a work session becomes an ordinary event in whichever Google/Outlook/iCloud/CalDAV calendar you've set as the default for scheduled tasks. Either way it is never written into the note.
So due_date ↔ due is the semantically correct default, and scheduled has no counterpart in Morgen's frontmatter contract at all. Making the target configurable seems worthwhile anyway, since some workflows treat scheduled as the primary date.
Two consequences worth designing for:
- This one is genuinely two-way. Morgen can write
due_dateback, so it needs the same idempotency and echo-suppression rules ascompleted— it is not a one-way derived write. - Times survive outbound, but guard the inbound write. Morgen's panel documents
due_dateasYYYY-MM-DD, yet it accepts and displays date+time (verified — a task set to 19:00 shows as "7:00 PM"). Soduecan be passed through unchanged, time included. What Morgen writes back when it changes the date is untested; if it emits date-only, an inbound write must preserve the existing time rather than flatten it.
And a limitation to document rather than solve: whichever field is not mapped stays invisible to Morgen, and Morgen's work sessions stay invisible to TaskNotes. If you schedule work in both places they will drift apart silently. In practice, pick one — and if you're using Morgen, Morgen is the one that can do multi-session scheduling.
Worth flagging because it's TaskNotes' own feature: since Morgen's sessions are real calendar events, they can collide with TaskNotes' Google Calendar export, which also creates events from scheduled or due (syncTrigger: "scheduled" | "due" | "both"). Point both at the same calendar and each task gets two events — one from TaskNotes' export, one from the Morgen session. Nothing breaks, but it's confusing, and a line in the docs would save someone the puzzle.
Two rules the sync needs
Morgen edits the same .md files that TaskNotes does, and each watches for the other's changes, so a naive implementation ping-pongs.
- Idempotency. Before changing anything, check whether status and boolean already agree; if so, do nothing. This kills the loop, and it gives the behaviour that matters: a task in
waitingreceivingcompleted: falseis already consistent, so leave it inwaitingrather than resetting it toopen. Without this rule every external write flattens intermediate states — defeating the point of the request. - Echo suppression. TaskNotes should ignore the change event matching what it just wrote.
Conflicts are last-writer-wins; inherent to file-based sync, worth documenting rather than solving.
Open questions
- Recurring tasks. Materialized occurrence notes need nothing — an occurrence note is an ordinary task, so it gets the key like any other. Virtual recurring parents need a decision on what a single boolean means for a series. I have a longer analysis of the cases and will add it as a comment rather than inline here.
- Skipped status (
isSkipped) — presumablycompleted: false, but worth stating. - Case-insensitivity. Morgen matches property names case-insensitively; TaskNotes presumably doesn't, so
Completedandcompletedcould both exist and Morgen would see both. - Archived tasks. Archive is a tag today (
archiveTag); [FR]: Use property to denote "archived" state #963 asks for a property. Archiving ≠ completion, so keep them orthogonal. - Value migration. TaskNotes doesn't rewrite existing notes when a status or priority value is renamed in settings, so old notes keep the old string and stop matching a configured value. That's the blocker for the priority toggle above, but it isn't really a Morgen problem — anyone renaming a value hits it today, and the v4.0.1 notes already describe the alphabetical-sorting situation as a workaround. Morgen may just be a good reason to build a value migration that's wanted anyway; happy to open that as its own issue if useful.
Non-goals
- Replacing or de-emphasising the status system. The extra keys are secondary and derived;
statusstays authoritative. - Any behaviour change for users who leave this disabled.
- Modifying existing property keys or values.
Willingness to help
@0xSnib offered a PR in #1098 and has already written the mapping and loop-suppression logic, so much of the hard thinking exists. I'm glad to test against a real Morgen two-way setup and to help with docs.
@callumalpass — would you prefer to reopen #1098 or track this here, and is there a solution you'd accept a PR for?
Appendix: verified Morgen behaviour (testing notes — skip unless implementing)
Behaviour observed directly in a current Morgen build, September 2026. The notes-as-tasks feature is labelled Beta, so re-check before relying on any of this.
Property parsing
durationaccepts a bare number or a duration string —60,30m,1.5h,1h30mall work. TaskNotes'timeEstimate(a number in minutes) can be copied verbatim; no string conversion needed.- All five priority values are distinct in the UI —
priority: lowestrenders as "Lowest", not bucketed into "Low". - An unrecognised priority value falls back to "default priority". So does an absent one: the two are indistinguishable. This is why the mapping should use
mediumfor a mid-rank priority rather than omitting the key — omission would signal "unparseable" where the user meant something specific. - Property names are matched case-insensitively, so
Completedandcompletedwould both be seen.
Identity and naming
- A note-task is named from its filename. A
titlefrontmatter property is ignored entirely (tested with atitlediffering from the filename — no effect). - TaskNotes'
storeTitleInFilenamedefaults totrue, so titles land in filenames and this works by default. Users who disable it and use Zettelkasten, timestamp or UUID filenames would see names like202609101423in Morgen. - Notes-as-tasks needs no unique IDs. Morgen's
🆔requirement applies only to inline tasks — the ID setting lives under "task inclusion", separate from note import.
Scope and direction
- Morgen has no calendar of its own and requires at least one connected account. Time-blocking a task writes an ordinary event into a connected Google/Outlook/iCloud/CalDAV calendar — chosen by a default-calendar preference, overridable per task — so work sessions are real calendar entries, not Morgen-internal state.
- Morgen cannot create a task in the vault; tasks made inside Morgen live in Morgen's own store. It does write into existing notes: completion state, done date,
due_date(optional, if enabled), and — for inline tasks — IDs. Whether it can writepriorityback is untested. morgen-tasks-include(checkbox) controls per-note inclusion, against a vault-wide include-or-exclude default.- Obsidian's own "excluded files" setting is honoured automatically.
- There is an "only import notes modified in the last N days" option. If enabled, older notes silently never appear — worth knowing when a task seems to be missing.
- For multi-session tasks, Morgen marks the parent complete only once every scheduled instance is complete, so there is exactly one boolean per note.
due_dateaccepts date+time, not justYYYY-MM-DDas the panel states — a task due at 19:00 displays as "7:00 PM".- A due date renders on the calendar as a non-blocking marker ("rendered by Morgen as due date for the task. It does not block time on your calendar"), separate from any session you schedule. Per-task there is a Block on the calendar action, and globally a Hide Obsidian task due dates toggle — so a timed due date appearing next to its own work session is a display preference, not something the frontmatter controls.
Feedback
Feedback on this FR always welcome, esp. if too long or want more extensive.
Metadata
Metadata
Assignees
Labels
Projects
- StatusShow more project fieldsNo status
Activity