Plainva 0.6.7: der Anker, der sich jedes Mal änderte
Jedes Neuverbinden vergibt eine neue Konto-Kennung. Das ist der ganze Fehler, und es brauchte einen Vault voller doppelter Aufgaben, um ihn zu sehen.
Eine Frage, zu spät gestellt
Plainva spiegelt Aufgaben von Google, Microsoft und CalDAV in Notizen. Jede dieser Notizen trägt im Frontmatter einen Anker, der sagt, für welche entfernte Aufgabe sie steht — damit der nächste Abgleich sie wiedererkennt, statt sie erneut anzulegen. Der Anker enthielt drei Dinge, und eines davon war die lokale Konto-Kennung, also die ID, die Plainva beim Verbinden eines Kontos vergibt.
Verbindet man dasselbe Konto neu, bekommt es eine neue. Am Konto ändert sich sonst nichts; die Kennung ändert sich. Von diesem Moment an passte kein Anker mehr zu seiner Aufgabe.
Das allein wäre zu verkraften gewesen, denn der Abgleich hat einen zweiten Weg, eine Aufgabe wiederzuerkennen: eine Zustandszeile, die festhält, was zuletzt synchronisiert wurde. Nur ist diese Zeile genauso gekeyt — und sie ist nicht die Datei, sie liegt im Index. Die eine Frage des Abgleichs, „gibt es eine Zustandszeile?”, lautete also nein nach einem Neuverbinden, nein auf einem zweiten Gerät, das nie eine hatte, und nein nach jedem Index-Neuaufbau. Drei sehr verschiedene Lagen, eine Antwort, eine Folge: noch einmal importieren.
Der Fix hat zwei Hälften. Der Anker trägt jetzt, was ein Neuverbinden überlebt — den Anbieter und die geprüfte Konto-Identität — und schreibt das alte Feld weiterhin mit, damit eine ältere Fassung es lesen kann, vergleicht es aber nie mehr. Und bevor etwas angelegt wird, fragt der Abgleich Deine Notizen: eine Abfrage über den Index, statt jede Notiz einmal je Aufgabe zu lesen. Hält eine Notiz die Aufgabe bereits, wird sie übernommen.
Beanspruchen mehrere Notizen dieselbe Aufgabe — und ein Vault, der ein paar Neuverbindungen hinter sich hat, tut das —, wird eine übernommen und der Rest gezählt, nicht gelöscht. Welche Deiner Doubletten die richtige ist, ist keine Frage, die ein Sync für sich beantworten sollte. Die Reihenfolge ist übrigens bewusst nicht lexikographisch: Steuern einreichen 2.md sortiert vor Steuern einreichen.md, die leere Kopie schlüge also die Notiz mit der Arbeit darin.
Löschen, in beide Richtungen
Bisher ließ das Löschen einer Aufgaben-Notiz die Aufgabe beim Anbieter stehen. Das war vertretbar, solange der Anker unzuverlässig war — und nicht mehr vertretbar, sobald er es nicht mehr ist.
Eine bestätigte Löschung nimmt die Aufgabe jetzt mit, nach acht Sekunden, mit Rückgängig im Hinweis. Sie nutzt dieselbe Warteschlange, die das Zurücknehmen einer gesendeten Mail trägt — mit der umgekehrten Regel am Sitzungsende. Mail flusht, weil der sichere Ausgang eines unterbrochenen Versands ist, dass die Nachricht rausgeht. Hier wird abgebrochen, weil der sichere Ausgang einer unterbrochenen Löschung ist, dass die Aufgabe noch existiert.
Eine bloß fehlende Datei löscht weiterhin nichts. Eine Datei kann aus Gründen verschwinden, die nichts mit Absicht zu tun haben — ein halb gelaufener Sync, ein außerhalb der App verschobener Ordner —, und keiner davon ist ein Grund, beim Anbieter etwas zu löschen.
Beides sind Desktop-Funktionen. Das Telefon kann eine Aufgabe beim Anbieter anlegen, spiegelt aber keine; dort ändert sich also nichts. Das steht im Paritäts-Katalog, statt unausgesprochen zu bleiben.
Zwei Dinge kamen beim Bauen heraus. IPimTarget.deleteTask existierte nicht — alle drei Anbieter konnten Aufgaben anlegen und ändern, keiner löschen. Und ein Abgleich-Zyklus innerhalb des Acht-Sekunden-Fensters hätte einen Grabstein geschrieben, der das Rückgängig überlebt — die zurückgeholte Notiz wäre für immer eine Waise gewesen.
Zwanzig Aufgaben, zwanzig Klicks
Eine Datenbank konnte immer nur eine Zeile. Zwanzig Aufgaben auf „erledigt” hieß zwanzig Klicks, zehn Einträge löschen zehn Rückfragen.
Der Klick war auf beiden Shells schon vergeben — am Desktop dem Inline-Editor, mobil dem Öffnen der Notiz —, die Auswahl brauchte also eine eigene Fläche. Sie bekam eine Kästchen-Spalte, die bei Hover erscheint, und das Halte-Blatt, dessen erster benannter Eintrag jetzt „Mehrere auswählen” ist.
Löschen war fast geschenkt; beide Ketten nahmen längst Arrays. Ein Wert für viele Zeilen ist der eigentliche Bau: beschränkte Nebenläufigkeit, Fortschritt, Abbruch und ein ehrlicher Teilfehler-Bericht. Tags, Listen, Mehrfachauswahl und Relationen sind bewusst ausgenommen — dort hieße „alle auf X setzen”, dass jeder bestehende Wert verschwindet; das ist keine Massenbearbeitung, sondern Datenverlust hinter einem Klick.
Umschalt+Klick für einen Bereich hat nur der Desktop, und zwar absichtlich. Ein Finger hat keine Modifikatortaste, und eine Zweitastenfolge — „von hier bis dort” — wäre eine Geste, die es sonst nirgends in der App gibt.
Der Ordner, der mit dem Konto ging
Der Cloud-Ordner war ein Feld der Zugangsdaten. Kein Geheimnis, aber im Schlüsselbund-Slot — also nahm ein entferntes Konto ihn mit. Das neu verbundene fand nichts zum Übernehmen, fiel auf seinen eingebauten Standard zurück und legte diesen Ordner an. Der Vault synchronisierte danach in ein frisches, leeres Remote, während das ursprüngliche unberührt danebenlag.
Der eine Weg zurück — den Ordner neu setzen — war selbst doppelt versperrt: das Feld ist schreibgeschützt, und der Picker verlangte einen eigenen Refresh-Token, den ein Konto bewusst nicht mehr hat. Drei Sperren, eine Ursache.
Verloren ging nichts, und das ist belegt statt gehofft: die Pull-Seite hält Löschungen an, sobald mehr als zehn und mehr als 20 % der bekannten Dateien in einem Listing fehlen.
Der Ordner liegt jetzt in den per-Vault-Einstellungen und überlebt das Entfernen eines Kontos, ein neu angelegter Ordner sagt es, und der Picker fragt denselben Broker wie der Sync. Beim Nachprüfen kam etwas heraus, das niemand gemeldet hatte: OneDrive und Dropbox legten ihren Wurzelordner auf beiden Shells stillschweigend an. Den Haken, der eine Neuanlage meldet, gab es nur an einem Zieltyp.
Die Anmeldung am Telefon
Ein Google-Kalenderkonto sagte direkt nach dem Anmelden wieder „Anmeldung abgelaufen”, und kein Neu-anmelden half.
Das Telefon fragte ausnahmslos zuerst den geteilten Konto-Token. Die frische Zustimmung war in den Dienst-Slot geschrieben worden; gelesen wurde der Konto-Slot. Und Google kann einen Grant beim Erneuern nicht verbreitern — der Token kommt mit genau den Rechten zurück, die die Zustimmung gab. Der Desktop hat gegen genau das seit dem 30. Juli einen Guard, mit einem Kommentar, der das Symptom wörtlich beschreibt. Er liegt jetzt an einer Stelle, die beide Shells rufen.
Der Ausweg war ebenfalls zugemauert: das Angebot, ein Konto zu vereinheitlichen, fragte, ob ein Konto-Token existiert — es verschwand also genau dann, wenn man es brauchte. Es fragt jetzt, ob der Token die Dienste trägt.
Datumsangaben, und eine Sache zum Wissen
{{date:dddd, D. MMMM YYYY}} schrieb „Friday, 31. July 2026” in einen deutschen Vault. Vorlagen schreiben Datumsangaben jetzt in der Sprache der App, und {{daily+1:Morgen}} kann eine Beschriftung tragen.
Die Wahl ist eine eigene Funktion statt eines Parameters, und das ist der interessante Teil. formatMoment trägt seit jeher die Notiz, sie sei bewusst nicht lokalisiert — mit einer sauberen Begründung: der Dateiname einer Tagesnotiz muss zu dem Format passen, mit dem er angelegt wurde, sonst findet Plainva die Notizen von gestern nicht mehr. Nur hat die Funktion zwei Aufrufer, und für den zweiten — ein Datum im Fließtext einer Notiz — trägt die Begründung nicht. Ein Locale-Argument ist etwas, das ein Aufrufer vergisst, und es im Dateinamen-Pfad zu vergessen ist der eine Fehler mit bleibenden Folgen.
Also: dddd und MMMM erzeugen andere Wörter als bisher, A/a wird in deutscher Oberfläche zu „vorm./nachm.”, und die Wochennummern ww/w folgen der Sprache — die ISO-Varianten WW/W bleiben stabil. Der Dateiname bleibt englisch. Bereits geschriebene Notizen sind unberührt; Text ist Text.
Der Rest
Anhänge öffnen, Ordner importieren. Dank an @davi-jorge-art für eine Meldung, deren Fehlerzeile vier Fehler auf einmal nannte — ein .md an einer .mp3, „Error creating” statt Öffnen, ein nicht dekodiertes %20, ein Path-Guard, der auf ein völlig legitimes ../ anschlug. Es ist ein Fehler: niemand hat den Pfad aufgelöst. Der richtige Resolver existiert seit Juli und lag im Lesemodus; der Editor schickte jeden nicht-http-Markdown-Link durch den Wiki-Pfad, dessen Miss-Zweig korrekterweise anbietet, eine fehlende Notiz anzulegen. Beide Ansichten fragen jetzt dieselbe Regel. Und davon unabhängig: der Ordner-Weg des Imports war nie funktionsfähig, für keine Quelle — der Assistent prüfte, dass Du einen Ordner gewählt hast, zeigte den Pfad an, und niemand las ihn.
Drei leisere Reparaturen am Telefon. Der Einstellungs-Import schreibt ein Journal, bevor er etwas ändert, und zwar als Datei statt als Preference — aus dem Hintergrund abgeräumt zu werden ist auf einem Telefon der Normalfall, und eine asynchron geschriebene Preference wäre zum Kill-Zeitpunkt nicht da. Ein Absturz kostet nicht mehr die Anschläge, die Du während eines laufenden Speicherns getippt hast. Und ein gelöschter Vault nimmt seine Geheimnisse mit — die Dienst-Slots jedes Kontos, die Entwürfe, die Journale, den Master-Key-Cache.
Unter der Haube: h2 0.4.16 schließt RUSTSEC-2026-0258, über reqwest im Sync-Pfad erreichbar.
Herunterladen
Plainva 0.6.7 liegt auf der Releases-Seite für Windows, macOS und Linux; die Desktop-App aktualisiert sich selbst. Android-Tester bekommen es über den internen Track, iOS über TestFlight.