Skip to content

[FR]: Optional Morgen-compatible completed property, without disturbing existing TaskNotes configuration #2312

Open

Description

@m4to-3pe

[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:

  1. 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.)
  2. 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 property completed and 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 title property (verified). TaskNotes' default storeTitleInFilename: true means 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 like 202609101423 in 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-obsidian plugin only hides task IDs in the editor — its entire settings interface is decorateIDs: '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_date back, so it needs the same idempotency and echo-suppression rules as completed — it is not a one-way derived write.
  • Times survive outbound, but guard the inbound write. Morgen's panel documents due_date as YYYY-MM-DD, yet it accepts and displays date+time (verified — a task set to 19:00 shows as "7:00 PM"). So due can 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.

  1. 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 waiting receiving completed: false is already consistent, so leave it in waiting rather than resetting it to open. Without this rule every external write flattens intermediate states — defeating the point of the request.
  2. 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) — presumably completed: false, but worth stating.
  • Case-insensitivity. Morgen matches property names case-insensitively; TaskNotes presumably doesn't, so Completed and completed could 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; status stays 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

  • duration accepts a bare number or a duration string — 60, 30m, 1.5h, 1h30m all 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: lowest renders 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 medium for 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 Completed and completed would both be seen.

Identity and naming

  • A note-task is named from its filename. A title frontmatter property is ignored entirely (tested with a title differing from the filename — no effect).
  • TaskNotes' storeTitleInFilename defaults to true, so titles land in filenames and this works by default. Users who disable it and use Zettelkasten, timestamp or UUID filenames would see names like 202609101423 in 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 write priority back 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_date accepts date+time, not just YYYY-MM-DD as 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions