Umgesetzt und veröffentlicht (2026-09-20, Commit e07d1ca, Stack 7.0.0-beta.11, MAJOR mit --breaking/--no-migration): docs verify, instructions verify und die volle pytest-Suite
(1410 Tests) grün. Alle Akzeptanzkriterien unten sind erfüllt und abgehakt.
Der Super-Productivity-Adapter liest heute ausschließlich einen periodischen Backup-Schnappschuss.
Läuft die Anwendung, gibt es daneben eine lokale REST-API mit dem aktuellen Zustand.
Die Wege schließen sich je System aus (Betreiber, 2026-09-20): eine headless bediente Instanz
geht immer auf die Datei, der Desktop immer auf die API. Kein Mischfall in Sicht. Also sagt jede
Instanz ausdrücklich, welchen Weg sie nimmt — und nimmt nur den.
Kompatibilität ist kein Kriterium (Betreiber, 2026-09-20): der Stack ist unveröffentlicht, die
Konfiguration wird überarbeitet statt erweitert. Das Feld ist deshalb Pflicht und trägt keinen
Default — eine Konfiguration, die den Weg nicht nennt, ist unvollständig, nicht „die alte".
Alles entschieden, nichts mehr offen. Die Verifikation gegen den SP-Quellcode ist am
2026-09-20 gelaufen (Ergebnis unten); der API-Weg trägt den Lesepfad vollständig und darf
angeboten werden.
Die Entscheidung
access im providereigenen Block, Pflicht, zwei Werte:
Wert
Liest
Schreibt
Eigene Felder
api
lokale REST-API, aktueller Zustand
Posten ja (#132); Projektanlage nein — siehe unten
api_base_url, api_token
snapshot
Backup-Schnappschuss, headless
nein — der Tracker ist von hier aus nur lesbar
backups_dir
Kein auto, kein Rückfall, keine Mischform. Der Name ist bewusst nicht read_via: das Feld
entscheidet beide Hälften, nicht nur das Lesen.
Der Block trägt nur die Felder seines Wegs. Ein backups_dir neben access: api wird beim
Lesen der Konfiguration abgelehnt, nicht ignoriert: eine Konfiguration, die zwei Wege beschreibt,
behauptet etwas, das nicht gilt — und beim nächsten Blick weiß niemand mehr, welcher Teil wirksam
ist.
api_token ist bei access: api Pflicht, nicht das heute optionale Durchreichfeld:
verifiziert ist, dass jeder Endpunkt außer GET /health einen Authorization: Bearer <token>
verlangt. Eine api-Instanz ohne Token kann nichts lesen.
Projektanlage bleibt auch auf dem API-Weg ein Menschenschritt
Korrektur gegenüber der früheren Fassung dieses Bodys (Verifikation 2026-09-20): es gibt
weiterhin keinPOST /projects. Registriert sind ausschließlich GET /status, GET /focus, GET|POST /task-control/*, GET|POST /tasks, GET|PATCH|DELETE /tasks/:id, GET /projects, GET /tags (src/app/core/electron/local-rest-api-handler.service.ts:420). Projekt-CRUD und
Tag-CRUD existieren nicht.
wikitool new project bleibt auf einer api-Instanz also bei HumanInterventionRequired/Exit 42
wie bisher. Was der API-Weg dort gewinnt, ist allein die Verifikation: --resume prüft über
einen Lesepfad, der den aktuellen Zustand sieht, statt gegen ein womöglich älteres Backup zu
laufen. Genau das war #134, und genau deshalb ist es aufgelöst.
new project verweigert auf einer snapshot-Instanz
Entschieden (Betreiber, 2026-09-20). Und zwar vollständig: kein Tracker-Projekt, und auch
keine Seite. Ein gewöhnlicher Validierungsfehler mit Exit 1, der auf die api-Instanz verweist —
kein Exit 42, denn hier wartet nichts auf eine Freigabe; das Kommando kann es an dieser Stelle
schlicht nicht. --resume verhält sich gleich.
Das sieht neben dem vorhandenen Verhalten „kein Tracker konfiguriert → nur die Seite, ausdrücklich
angesagt" inkonsistent aus und ist es nicht. Der Unterschied ist, ob überhaupt gejoint wird:
Kein Tracker konfiguriert:review läuft gar nicht (es scheitert an der fehlenden
Konfiguration). Eine Seite ohne Tracker-Projekt ist dort der Normalzustand und fällt niemandem
auf die Füße.
access: snapshot:review läuft. Eine Seite ohne Tracker-Projekt ist dort genau das, was
Prüfung 4 (no_open_loop) als Befund meldet — die Hälfte eines Paars, angelegt von einer Instanz,
die die andere Hälfte gar nicht anlegen kann.
Kosten: praktisch keine. An einer headless bedienten Instanz sitzt niemand, der ein Projekt anlegt.
db_path wird gestrichen
Entschieden (Betreiber, 2026-09-20). backups_dir ist damit für access: snapshot das einzige und
pflichtige Feld, und latest_snapshot_path() verliert seinen Zweig.
Was dieser Verzicht tragend macht (Verifikation 2026-09-20): electron/backup.ts:148 schreibt <userData>/backups/YYYY-MM-DD_HHmmss.json — ohne Präfix, lexikalisch also gleich chronologisch.
Zwei Haken, die heute niemand bemerkt hätte, weil db_path sie umgehen konnte:
SP selbst sortiert in listBackupFiles nach mtime, nicht lexikalisch. Der zitierte Kommentar
„timestamps sort lexically" steht an der Windows-Store-Pfadwahl, nicht an der Sortierung.
Die manuellen Exporte heißen sp-backup_*, sp-backup-anonymized_*, sp-pre-migration-backup_* (electron/shared-with-frontend/get-backup-timestamp.ts). Die
sortieren hinter jeden reinen Zeitstempel. Eine einzige dorthin abgelegte Exportdatei würde
den Leser dauerhaft auf einen alten Stand nageln.
Also: der Glob wird auf das Zeitstempelmuster eingeschränkt statt auf *.json.
Das Ergebnis der Quellcode-Verifikation (2026-09-20)
Gegen super-productivity/super-productivity@master. Die Routen liegen nicht in electron/local-rest-api.ts — das ist nur Transport, /health und Token-Prüfung — sondern im
Renderer unter src/app/core/electron/local-rest-api-handler.service.ts. Die Pfadangabe im
Modul-Docstring des Adapters ist entsprechend falsch und wird nachgezogen.
Gebraucht
Ergebnis
Projektliste mit Anlagedatum
ja — GET /projects liefert die vollen Project-Objekte; created?: number (packages/plugin-api/src/types.ts)
Offene Posten je Projekt
ja — GET /tasks liefert volle Task-Objekte, ?projectId=, includeDone per Default false
WAITING → Tag waiting
ja — GET /tags liefert volle Tag-Objekte mit title; task.tagIds liegt am Task
follow_up_at
lesbar — aber das gelesene Feld ist falsch gewählt, siehe #135
Someday → backlogTaskIds
ja — backlogTaskIds: string[] am Project-Objekt
api_token
ja — alles außer GET /health verlangt Authorization: Bearer
Alle drei Listen-Endpunkte geben die NgRx-Store-Objekte unverändert zurück, also strukturell
dieselben Datensätze, die auch im Schnappschuss stehen. Der Äquivalenztest hat es dadurch leicht —
mit genau zwei Ausnahmen, die er sonst als Fehlschlag meldet:
Divergenz 1: archivierte Projekte
GET /projects läuft über selectUnarchivedProjects und filtert isArchived
(src/app/features/project/store/project.selectors.ts:16). Der Schnappschuss-Leser iteriert heute alleproject-Entities. Prüfung 3 (unpaged_project_weeks) meldete je nach Weg verschieden viel.
Entschieden (Betreiber, 2026-09-20): beide filtern isArchived. Der Schnappschuss-Weg zieht
auf das API-Verhalten nach — ein archiviertes Projekt soll keinen unpaged_project-Befund
erzeugen. Das ist eine Verhaltensänderung an review für bestehende Schnappschuss-Instanzen
(weniger Befunde) und braucht einen Changelog-Eintrag, der sie benennt.
Divergenz 2: Unteraufgaben
Unteraufgaben erben projectId vom Elternteil (addSubTask erzwingt es), GET /tasks?projectId=
liefert sie also mit; project.taskIds trägt nur die oberste Ebene. Der API-Weg zählt gegen project.taskIds statt gegen den projectId-Filter — dieselbe Wahrheitsquelle wie der Dateiweg.
Was diese Form verschwinden lässt
Ein erster Entwurf hatte eine Laufzeit-Stufung mit Rückfall und drei Bedingungen daran. Zwei davon
entfallen vollständig:
Die Quelle kann nicht mitten im Lauf kippen.run_review ruft nacheinander projects(), open_items() je Projekt und someday_items(); mit einem Rückfall hätte ein Schließen der
Anwendung dazwischen einen Bericht aus zwei Welten erzeugt — Befunde aus Prüfung 3/4, die echt
aussehen und keine sind. Ohne Laufzeitwahl ist das keine Bedingung mehr, sondern eine Eigenschaft
der Konstruktion.
Das Verifikationsfenster aus #134 ist zu.new project --resume prüft über den Lesepfad, ob
ein Mensch das Projekt tatsächlich angelegt hat. Auf einer snapshot-Instanz konnte diese Prüfung
gegen ein Backup von vor dem Schritt des Menschen laufen und „immer noch nicht da" melden. Da es
die Anlage dort nicht mehr gibt, läuft verify() nur noch, wo live gelesen wird. #134 ist damit
aufgelöst und geschlossen; die dort benannte Korrektur an der Begründung in tasks/protocol.py
gehört in dieses Paket.
Was bleibt, ist der Äquivalenztest — und er wird dringlicher: wenn die beiden Wege auf
verschiedenen Maschinen leben, sieht sie in der Praxis nie jemand nebeneinander. Nur ein Test hält
sie zusammen. Dazu die Herkunftsangabe: die Antwort nennt ihren Weg, und bei snapshot dessen
Alter.
Was der Verzicht auf den Rückfall kostet — und warum er richtig ist
Ist die Anwendung auf einer api-Instanz geschlossen, gibt es keine Antwort: Provider nicht
erreichbar, betroffene Prüfungen übersprungen, Bericht als INCOMPLETE gekennzeichnet, review
endet mit Exit 1. Das ist das vorhandene, ausdrücklich so gebaute Verhalten.
Und es ist die bessere Hälfte des Tauschs. Ein Rückfall hätte an derselben Stelle eine Antwort aus
einem zwei Tage alten Schnappschuss geliefert, die aussieht wie eine frische — genau die leise
Falschheit, gegen die der Stack sonst überall anläuft (commit: null, der INCOMPLETE-Block, der
Commit-Stempel an jeder MCP-Antwort). Die Abhilfe liegt beim Menschen und ist trivial: die Anwendung
öffnen.
Verifiziert dazu: die API kennt mit 503 APP_NOT_READY einen eigenen Zustand für „Server da,
Renderer noch nicht" — „nicht erreichbar" und „antwortet Unsinn" sind dort bereits getrennt.
#131: die headless bediente Instanz sagt access: snapshot. Dass sie den Tracker nicht
schreiben kann, folgt damit aus der Konfiguration statt aus der Netztopologie — dieselbe
Eigenschaft, ausdrücklich statt beiläufig.
#132: der Schreibweg setzt access: api voraus. Ein Posten, der über die API angelegt wird,
ist dort auch sofort wieder lesbar — das Kriterium „der Posten taucht anschließend in wikitool review auf" ist damit sauber erfüllbar statt an ein Backup-Intervall gekoppelt.
Verifiziert: POST /tasks existiert, tagIds ist schreibbar (der waiting-Tag muss allerdings
schon existieren — Tags sind über die API nicht anlegbar), isAddToBacklog ist fest false,
ein Posten landet also nie im Backlog.
#135:follow_up_at liest heute remindAt, und das ist über die API nicht schreibbar. Erst
mit der dortigen Korrektur auf dueWithTime/dueDay wird #132s Schreibweg vollständig. Berührt
dieses Issue nicht — hier wird nur der Weg gewählt, nicht das Feld. Umgesetzt zusammen mit
diesem Issue, im selben Publish.
#128: nicht berührt. Welchen Transport ein Adapter innen wählt, geht TaskReader nichts an —
keine Änderung an tasks/protocol.py außer der Korrektur ihrer Begründung. Die Regel-1-Korrektur
aus #135 wurde in #128s eigenem Body nachgezogen (dort, nicht hier, da sie den Begriff bindet, den
ein zweiter Adapter prüfen muss).
Akzeptanzkriterien
access ist Pflicht; eine Konfiguration ohne das Feld scheitert beim Lesen der
Konfiguration mit einer Meldung, die beide Werte nennt — nicht erst beim ersten Zugriff.
access: api liest ausschließlich über die API, access: snapshot ausschließlich aus der
Datei. Kein Codepfad wechselt den Weg, unter keinem Fehlerzustand.
Ein Feld, das nicht zum gewählten Weg gehört, wird beim Lesen der Konfiguration abgelehnt.
api_token ist bei access: api Pflicht; eine Konfiguration ohne Token scheitert beim Lesen
der Konfiguration, nicht erst am ersten 401.
db_path existiert nicht mehr — weder im Code, noch in _EXPECTED_PROVIDER_CONFIG, noch in
einem Test oder einer Dokumentationsstelle. Die Fixtures pinnen stattdessen ein Verzeichnis
mit genau einer Datei.
latest_snapshot_path() wählt nur Dateien, deren Name dem Muster YYYY-MM-DD_HHmmss.json
entspricht. Ein Test legt eine sp-backup_*.json neben eine neuere Zeitstempeldatei und
prüft, dass die Zeitstempeldatei gewinnt.
wikitool new project legt bei access: snapshotnichts an — kein Tracker-Projekt und
keine Seite — und endet mit Exit 1 und einer Meldung, die auf die api-Instanz verweist. --resume ebenso. Ein Test prüft, dass nach dem Aufruf keine Datei entstanden ist.
Bei access: api verhält sich new project unverändert: HumanInterventionRequired/Exit 42,
und --resume verifiziert über den Live-Lesepfad. Ein Test hält fest, dass dieser Weg kein POST /projects versucht.
Beide Wege liefern für dieselbe Fixture identische ProjectSummary/OpenItems/ SomedayItem-Werte; kein Test braucht eine laufende Super-Productivity-Instanz — der
API-Weg wird gegen eine Attrappe geprüft.
Beide Wege blenden Projekte mit isArchived: true aus. Ein Test hält das für beide fest,
und der Changelog-Eintrag benennt die Verhaltensänderung an Prüfung 3.
Der API-Weg zählt offene Posten gegen project.taskIds, nicht gegen den projectId-Filter;
eine Fixture mit Unteraufgaben zeigt, dass beide Wege dieselbe Zahl liefern.
Jede Antwort trägt, aus welchem Weg sie stammt, und bei snapshot dessen Alter; wikitool review zeigt es in beiden Ausgabeformen.
Ein unerwartetes API-Antwortformat scheitert laut, genau wie eine unbekannte
Schnappschuss-Form — „nicht erreichbar" und „antwortet Unsinn" bleiben verschiedene Zustände,
und keiner von beiden wechselt den Weg.
Die Begründung in tasks/protocol.py („re-reads on every call, so a caller checking verify() ... sees the current state") sagt, was tatsächlich gilt — die aus #134 übernommene
Korrektur.
wikitool doctor meldet den konfigurierten Weg und dessen Zustand; der andere ist kein
Befund. Weiterhin nie ein FAIL — eine geschlossene Anwendung ist kein Fehler.
Nachgezogen: der Modul-Docstring von superproductivity.py, tools/CONTRACT.md
(doctor-Zeile, der Fehlervertrag zu review, die new project-Zeile samt ihrem neuen
Exit-1-Fall), INSTALL.md § Konfiguration.
docs verify, instructions verify, pytest grün. Version gebumpt — MAJOR, mit --breaking (das neue Pflichtfeld und das gestrichene db_path) und --no-migration
(keine kb/-Inhaltsänderung; der Bruch bleibt auf .wikitool-tasks.json beschränkt).
Vorher zu klären
Nichts mehr. Die einzige offene Frage — ob die API alle Lesefragen beantwortet — ist am 2026-09-20
gegen den Quellcode verifiziert; das Ergebnis steht oben.
Entschieden (2026-09-20):access statt read_via, Pflicht ohne Default; kein Rückfall, kein auto; Schreibweg nur bei access: api; new project verweigert auf snapshot vollständig und
bleibt auf api ein verifizierter Menschenschritt; db_path gestrichen und der Glob dafür
gehärtet; api_token bei access: api Pflicht; beide Wege filtern isArchived.
Abhängigkeiten
Keine blockierenden. Berührungspunkte: #132 (der Schreibweg), #135 (das Feld hinter follow_up_at — umgesetzt im selben Publish), #131 (die headless bediente Instanz), #128 (nicht
berührt, außer der nachgezogenen Regel-1-Korrektur). #134 ist durch dieses Issue aufgelöst und
geschlossen.
Umsetzung
chemenu.tasks.superproductivity: SuperProductivityConfig.access (Pflichtfeld, zwei Werte, je
eigene Feldmenge), SuperProductivityReader (Schnappschuss, gehärteter Glob, isArchived-Filter),
neu SuperProductivityApiReader (REST-API, gegen eine Attrappe getestet), chemenu.tasks.protocol
(neu ReadSource, TaskReader.source()), chemenu.tasks.build_reader/build_writer (Dispatch
nach access, build_writer verweigert auf snapshot), chemenu.commands.new_page
(_ensure_tracker_project baut den Writer vor dem Kollisions-Check, damit die access: snapshot-Verweigerung zuerst greift), chemenu.commands.doctor (check_tasks_provider
meldet nur den konfigurierten Weg), chemenu.review/chemenu.commands.review_cmd
(ReviewReport.source, gerendert in Text und --json). Tests in test_superproductivity.py
(rundum neu, inkl. API-Stub-Server und Äquivalenztest), test_new_page.py, test_review.py, test_doctor.py, test_tasks_config.py. Doku: INSTALL.md § Konfiguration, tools/CONTRACT.md (Zeilen review, new project, doctor, Fehlervertrag new project),
Modul-Docstring von superproductivity.py. Version: 7.0.0-beta.10 → 7.0.0-beta.11 (MAJOR).
Commit e07d1ca.
**Umgesetzt und veröffentlicht** (2026-09-20, Commit `e07d1ca`, Stack `7.0.0-beta.11`, MAJOR mit
`--breaking`/`--no-migration`): `docs verify`, `instructions verify` und die volle `pytest`-Suite
(1410 Tests) grün. Alle Akzeptanzkriterien unten sind erfüllt und abgehakt.
Der Super-Productivity-Adapter liest heute ausschließlich einen periodischen Backup-Schnappschuss.
Läuft die Anwendung, gibt es daneben eine lokale REST-API mit dem *aktuellen* Zustand.
**Die Wege schließen sich je System aus** (Betreiber, 2026-09-20): eine headless bediente Instanz
geht immer auf die Datei, der Desktop immer auf die API. Kein Mischfall in Sicht. Also sagt jede
Instanz **ausdrücklich**, welchen Weg sie nimmt — und nimmt nur den.
**Kompatibilität ist kein Kriterium** (Betreiber, 2026-09-20): der Stack ist unveröffentlicht, die
Konfiguration wird überarbeitet statt erweitert. Das Feld ist deshalb **Pflicht** und trägt keinen
Default — eine Konfiguration, die den Weg nicht nennt, ist unvollständig, nicht „die alte".
**Alles entschieden, nichts mehr offen.** Die Verifikation gegen den SP-Quellcode ist am
2026-09-20 gelaufen (Ergebnis unten); der API-Weg trägt den Lesepfad vollständig und darf
angeboten werden.
## Die Entscheidung
`access` im providereigenen Block, Pflicht, zwei Werte:
| Wert | Liest | Schreibt | Eigene Felder |
|---|---|---|---|
| `api` | lokale REST-API, aktueller Zustand | Posten ja (#132); **Projektanlage nein** — siehe unten | `api_base_url`, `api_token` |
| `snapshot` | Backup-Schnappschuss, headless | **nein** — der Tracker ist von hier aus nur lesbar | `backups_dir` |
Kein `auto`, kein Rückfall, keine Mischform. Der Name ist bewusst nicht `read_via`: das Feld
entscheidet beide Hälften, nicht nur das Lesen.
**Der Block trägt nur die Felder seines Wegs.** Ein `backups_dir` neben `access: api` wird beim
Lesen der Konfiguration abgelehnt, nicht ignoriert: eine Konfiguration, die zwei Wege beschreibt,
behauptet etwas, das nicht gilt — und beim nächsten Blick weiß niemand mehr, welcher Teil wirksam
ist.
**`api_token` ist bei `access: api` Pflicht**, nicht das heute optionale Durchreichfeld:
verifiziert ist, dass jeder Endpunkt außer `GET /health` einen `Authorization: Bearer <token>`
verlangt. Eine `api`-Instanz ohne Token kann nichts lesen.
### Projektanlage bleibt auch auf dem API-Weg ein Menschenschritt
**Korrektur gegenüber der früheren Fassung dieses Bodys** (Verifikation 2026-09-20): es gibt
weiterhin **kein** `POST /projects`. Registriert sind ausschließlich `GET /status`, `GET /focus`,
`GET|POST /task-control/*`, `GET|POST /tasks`, `GET|PATCH|DELETE /tasks/:id`, `GET /projects`,
`GET /tags` (`src/app/core/electron/local-rest-api-handler.service.ts:420`). Projekt-CRUD und
Tag-CRUD existieren nicht.
`wikitool new project` bleibt auf einer `api`-Instanz also bei `HumanInterventionRequired`/Exit 42
wie bisher. Was der API-Weg dort gewinnt, ist allein die **Verifikation**: `--resume` prüft über
einen Lesepfad, der den aktuellen Zustand sieht, statt gegen ein womöglich älteres Backup zu
laufen. Genau das war #134, und genau deshalb ist es aufgelöst.
### `new project` verweigert auf einer `snapshot`-Instanz
Entschieden (Betreiber, 2026-09-20). Und zwar **vollständig**: kein Tracker-Projekt, **und auch
keine Seite**. Ein gewöhnlicher Validierungsfehler mit Exit 1, der auf die `api`-Instanz verweist —
kein Exit 42, denn hier wartet nichts auf eine Freigabe; das Kommando kann es an dieser Stelle
schlicht nicht. `--resume` verhält sich gleich.
Das sieht neben dem vorhandenen Verhalten „kein Tracker konfiguriert → nur die Seite, ausdrücklich
angesagt" inkonsistent aus und ist es nicht. Der Unterschied ist, ob überhaupt gejoint wird:
- **Kein Tracker konfiguriert:** `review` läuft gar nicht (es scheitert an der fehlenden
Konfiguration). Eine Seite ohne Tracker-Projekt ist dort der Normalzustand und fällt niemandem
auf die Füße.
- **`access: snapshot`:** `review` läuft. Eine Seite ohne Tracker-Projekt ist dort genau das, was
Prüfung 4 (`no_open_loop`) als Befund meldet — die Hälfte eines Paars, angelegt von einer Instanz,
die die andere Hälfte gar nicht anlegen kann.
Kosten: praktisch keine. An einer headless bedienten Instanz sitzt niemand, der ein Projekt anlegt.
### `db_path` wird gestrichen
Entschieden (Betreiber, 2026-09-20). `backups_dir` ist damit für `access: snapshot` das einzige und
pflichtige Feld, und `latest_snapshot_path()` verliert seinen Zweig.
**Was dieser Verzicht tragend macht** (Verifikation 2026-09-20): `electron/backup.ts:148` schreibt
`<userData>/backups/YYYY-MM-DD_HHmmss.json` — ohne Präfix, lexikalisch also gleich chronologisch.
Zwei Haken, die heute niemand bemerkt hätte, weil `db_path` sie umgehen konnte:
- SP selbst sortiert in `listBackupFiles` nach **mtime**, nicht lexikalisch. Der zitierte Kommentar
„timestamps sort lexically" steht an der Windows-Store-Pfadwahl, nicht an der Sortierung.
- Die manuellen Exporte heißen `sp-backup_*`, `sp-backup-anonymized_*`,
`sp-pre-migration-backup_*` (`electron/shared-with-frontend/get-backup-timestamp.ts`). Die
sortieren **hinter** jeden reinen Zeitstempel. Eine einzige dorthin abgelegte Exportdatei würde
den Leser dauerhaft auf einen alten Stand nageln.
Also: der Glob wird auf das Zeitstempelmuster eingeschränkt statt auf `*.json`.
## Das Ergebnis der Quellcode-Verifikation (2026-09-20)
Gegen `super-productivity/super-productivity@master`. Die Routen liegen **nicht** in
`electron/local-rest-api.ts` — das ist nur Transport, `/health` und Token-Prüfung — sondern im
Renderer unter `src/app/core/electron/local-rest-api-handler.service.ts`. Die Pfadangabe im
Modul-Docstring des Adapters ist entsprechend falsch und wird nachgezogen.
| Gebraucht | Ergebnis |
|---|---|
| Projektliste mit Anlagedatum | **ja** — `GET /projects` liefert die vollen `Project`-Objekte; `created?: number` (`packages/plugin-api/src/types.ts`) |
| Offene Posten je Projekt | **ja** — `GET /tasks` liefert volle `Task`-Objekte, `?projectId=`, `includeDone` per Default `false` |
| `WAITING` → Tag `waiting` | **ja** — `GET /tags` liefert volle `Tag`-Objekte mit `title`; `task.tagIds` liegt am Task |
| `follow_up_at` | **lesbar** — aber das gelesene Feld ist falsch gewählt, siehe #135 |
| Someday → `backlogTaskIds` | **ja** — `backlogTaskIds: string[]` am Project-Objekt |
| `api_token` | **ja** — alles außer `GET /health` verlangt `Authorization: Bearer` |
Alle drei Listen-Endpunkte geben die NgRx-Store-Objekte unverändert zurück, also **strukturell
dieselben Datensätze**, die auch im Schnappschuss stehen. Der Äquivalenztest hat es dadurch leicht —
mit genau zwei Ausnahmen, die er sonst als Fehlschlag meldet:
### Divergenz 1: archivierte Projekte
`GET /projects` läuft über `selectUnarchivedProjects` und filtert `isArchived`
(`src/app/features/project/store/project.selectors.ts:16`). Der Schnappschuss-Leser iteriert heute
*alle* `project`-Entities. Prüfung 3 (`unpaged_project_weeks`) meldete je nach Weg verschieden viel.
**Entschieden (Betreiber, 2026-09-20): beide filtern `isArchived`.** Der Schnappschuss-Weg zieht
auf das API-Verhalten nach — ein archiviertes Projekt soll keinen `unpaged_project`-Befund
erzeugen. Das ist eine Verhaltensänderung an `review` für bestehende Schnappschuss-Instanzen
(weniger Befunde) und braucht einen Changelog-Eintrag, der sie benennt.
### Divergenz 2: Unteraufgaben
Unteraufgaben erben `projectId` vom Elternteil (`addSubTask` erzwingt es), `GET /tasks?projectId=`
liefert sie also mit; `project.taskIds` trägt nur die oberste Ebene. Der API-Weg zählt gegen
`project.taskIds` statt gegen den `projectId`-Filter — dieselbe Wahrheitsquelle wie der Dateiweg.
## Was diese Form verschwinden lässt
Ein erster Entwurf hatte eine Laufzeit-Stufung mit Rückfall und drei Bedingungen daran. Zwei davon
entfallen vollständig:
- **Die Quelle kann nicht mitten im Lauf kippen.** `run_review` ruft nacheinander `projects()`,
`open_items()` je Projekt und `someday_items()`; mit einem Rückfall hätte ein Schließen der
Anwendung dazwischen einen Bericht aus zwei Welten erzeugt — Befunde aus Prüfung 3/4, die echt
aussehen und keine sind. Ohne Laufzeitwahl ist das keine Bedingung mehr, sondern eine Eigenschaft
der Konstruktion.
- **Das Verifikationsfenster aus #134 ist zu.** `new project --resume` prüft über den Lesepfad, ob
ein Mensch das Projekt tatsächlich angelegt hat. Auf einer `snapshot`-Instanz konnte diese Prüfung
gegen ein Backup von *vor* dem Schritt des Menschen laufen und „immer noch nicht da" melden. Da es
die Anlage dort nicht mehr gibt, läuft `verify()` nur noch, wo live gelesen wird. #134 ist damit
aufgelöst und geschlossen; die dort benannte Korrektur an der Begründung in `tasks/protocol.py`
gehört in dieses Paket.
**Was bleibt, ist der Äquivalenztest** — und er wird *dringlicher*: wenn die beiden Wege auf
verschiedenen Maschinen leben, sieht sie in der Praxis nie jemand nebeneinander. Nur ein Test hält
sie zusammen. Dazu die Herkunftsangabe: die Antwort nennt ihren Weg, und bei `snapshot` dessen
Alter.
## Was der Verzicht auf den Rückfall kostet — und warum er richtig ist
Ist die Anwendung auf einer `api`-Instanz geschlossen, gibt es keine Antwort: Provider nicht
erreichbar, betroffene Prüfungen übersprungen, Bericht als `INCOMPLETE` gekennzeichnet, `review`
endet mit Exit 1. Das ist das vorhandene, ausdrücklich so gebaute Verhalten.
Und es ist die bessere Hälfte des Tauschs. Ein Rückfall hätte an derselben Stelle eine Antwort aus
einem zwei Tage alten Schnappschuss geliefert, die aussieht wie eine frische — genau die leise
Falschheit, gegen die der Stack sonst überall anläuft (`commit: null`, der `INCOMPLETE`-Block, der
Commit-Stempel an jeder MCP-Antwort). Die Abhilfe liegt beim Menschen und ist trivial: die Anwendung
öffnen.
Verifiziert dazu: die API kennt mit `503 APP_NOT_READY` einen eigenen Zustand für „Server da,
Renderer noch nicht" — „nicht erreichbar" und „antwortet Unsinn" sind dort bereits getrennt.
## Konfigurationsform
```json
{
"schema": 1,
"provider": "superproductivity",
"thresholds": { "stalled_waiting_days": 14, "unpaged_project_weeks": 3, "someday_stale_months": 5 },
"superproductivity": {
"access": "api",
"api_base_url": "http://127.0.0.1:3876",
"api_token": "..."
}
}
```
```json
"superproductivity": {
"access": "snapshot",
"backups_dir": "~/.config/superProductivity/backups"
}
```
## Berührungspunkte
- **#131:** die headless bediente Instanz sagt `access: snapshot`. Dass sie den Tracker nicht
schreiben kann, folgt damit aus der Konfiguration statt aus der Netztopologie — dieselbe
Eigenschaft, ausdrücklich statt beiläufig.
- **#132:** der Schreibweg setzt `access: api` voraus. Ein Posten, der über die API angelegt wird,
ist dort auch sofort wieder lesbar — das Kriterium „der Posten taucht anschließend in
`wikitool review` auf" ist damit sauber erfüllbar statt an ein Backup-Intervall gekoppelt.
Verifiziert: `POST /tasks` existiert, `tagIds` ist schreibbar (der `waiting`-Tag muss allerdings
schon existieren — Tags sind über die API nicht anlegbar), `isAddToBacklog` ist fest `false`,
ein Posten landet also nie im Backlog.
- **#135:** `follow_up_at` liest heute `remindAt`, und das ist über die API nicht schreibbar. Erst
mit der dortigen Korrektur auf `dueWithTime`/`dueDay` wird #132s Schreibweg vollständig. Berührt
dieses Issue nicht — hier wird nur der Weg gewählt, nicht das Feld. **Umgesetzt zusammen mit
diesem Issue, im selben Publish.**
- **#128:** nicht berührt. Welchen Transport ein Adapter innen wählt, geht `TaskReader` nichts an —
keine Änderung an `tasks/protocol.py` außer der Korrektur ihrer Begründung. Die Regel-1-Korrektur
aus #135 wurde in #128s eigenem Body nachgezogen (dort, nicht hier, da sie den Begriff bindet, den
ein zweiter Adapter prüfen muss).
## Akzeptanzkriterien
- [x] `access` ist Pflicht; eine Konfiguration ohne das Feld scheitert beim **Lesen der
Konfiguration** mit einer Meldung, die beide Werte nennt — nicht erst beim ersten Zugriff.
- [x] `access: api` liest ausschließlich über die API, `access: snapshot` ausschließlich aus der
Datei. Kein Codepfad wechselt den Weg, unter keinem Fehlerzustand.
- [x] Ein Feld, das nicht zum gewählten Weg gehört, wird beim Lesen der Konfiguration abgelehnt.
- [x] `api_token` ist bei `access: api` Pflicht; eine Konfiguration ohne Token scheitert beim Lesen
der Konfiguration, nicht erst am ersten `401`.
- [x] `db_path` existiert nicht mehr — weder im Code, noch in `_EXPECTED_PROVIDER_CONFIG`, noch in
einem Test oder einer Dokumentationsstelle. Die Fixtures pinnen stattdessen ein Verzeichnis
mit genau einer Datei.
- [x] `latest_snapshot_path()` wählt nur Dateien, deren Name dem Muster `YYYY-MM-DD_HHmmss.json`
entspricht. Ein Test legt eine `sp-backup_*.json` neben eine neuere Zeitstempeldatei und
prüft, dass die Zeitstempeldatei gewinnt.
- [x] `wikitool new project` legt bei `access: snapshot` **nichts** an — kein Tracker-Projekt und
keine Seite — und endet mit Exit 1 und einer Meldung, die auf die `api`-Instanz verweist.
`--resume` ebenso. Ein Test prüft, dass nach dem Aufruf keine Datei entstanden ist.
- [x] Bei `access: api` verhält sich `new project` unverändert: `HumanInterventionRequired`/Exit 42,
und `--resume` verifiziert über den Live-Lesepfad. Ein Test hält fest, dass dieser Weg **kein**
`POST /projects` versucht.
- [x] Beide Wege liefern für dieselbe Fixture identische `ProjectSummary`/`OpenItems`/
`SomedayItem`-Werte; **kein Test braucht eine laufende Super-Productivity-Instanz** — der
API-Weg wird gegen eine Attrappe geprüft.
- [x] Beide Wege blenden Projekte mit `isArchived: true` aus. Ein Test hält das für beide fest,
und der Changelog-Eintrag benennt die Verhaltensänderung an Prüfung 3.
- [x] Der API-Weg zählt offene Posten gegen `project.taskIds`, nicht gegen den `projectId`-Filter;
eine Fixture mit Unteraufgaben zeigt, dass beide Wege dieselbe Zahl liefern.
- [x] Jede Antwort trägt, aus welchem Weg sie stammt, und bei `snapshot` dessen Alter;
`wikitool review` zeigt es in beiden Ausgabeformen.
- [x] Ein unerwartetes API-Antwortformat scheitert laut, genau wie eine unbekannte
Schnappschuss-Form — „nicht erreichbar" und „antwortet Unsinn" bleiben verschiedene Zustände,
und keiner von beiden wechselt den Weg.
- [x] Die Begründung in `tasks/protocol.py` („re-reads on every call, so a caller checking
`verify()` ... sees the current state") sagt, was tatsächlich gilt — die aus #134 übernommene
Korrektur.
- [x] `wikitool doctor` meldet den konfigurierten Weg und dessen Zustand; der andere ist kein
Befund. Weiterhin nie ein `FAIL` — eine geschlossene Anwendung ist kein Fehler.
- [x] Nachgezogen: der Modul-Docstring von `superproductivity.py`, `tools/CONTRACT.md`
(`doctor`-Zeile, der Fehlervertrag zu `review`, die `new project`-Zeile samt ihrem neuen
Exit-1-Fall), `INSTALL.md` § Konfiguration.
- [x] `docs verify`, `instructions verify`, `pytest` grün. Version gebumpt — MAJOR, mit
`--breaking` (das neue Pflichtfeld und das gestrichene `db_path`) und `--no-migration`
(keine `kb/`-Inhaltsänderung; der Bruch bleibt auf `.wikitool-tasks.json` beschränkt).
## Vorher zu klären
Nichts mehr. Die einzige offene Frage — ob die API alle Lesefragen beantwortet — ist am 2026-09-20
gegen den Quellcode verifiziert; das Ergebnis steht oben.
**Entschieden (2026-09-20):** `access` statt `read_via`, Pflicht ohne Default; kein Rückfall, kein
`auto`; Schreibweg nur bei `access: api`; `new project` verweigert auf `snapshot` vollständig und
bleibt auf `api` ein verifizierter Menschenschritt; `db_path` gestrichen und der Glob dafür
gehärtet; `api_token` bei `access: api` Pflicht; beide Wege filtern `isArchived`.
## Abhängigkeiten
Keine blockierenden. Berührungspunkte: #132 (der Schreibweg), #135 (das Feld hinter
`follow_up_at` — umgesetzt im selben Publish), #131 (die headless bediente Instanz), #128 (nicht
berührt, außer der nachgezogenen Regel-1-Korrektur). #134 ist durch dieses Issue aufgelöst und
geschlossen.
## Umsetzung
`chemenu.tasks.superproductivity`: `SuperProductivityConfig.access` (Pflichtfeld, zwei Werte, je
eigene Feldmenge), `SuperProductivityReader` (Schnappschuss, gehärteter Glob, `isArchived`-Filter),
neu `SuperProductivityApiReader` (REST-API, gegen eine Attrappe getestet), `chemenu.tasks.protocol`
(neu `ReadSource`, `TaskReader.source()`), `chemenu.tasks.build_reader`/`build_writer` (Dispatch
nach `access`, `build_writer` verweigert auf `snapshot`), `chemenu.commands.new_page`
(`_ensure_tracker_project` baut den Writer vor dem Kollisions-Check, damit die
`access: snapshot`-Verweigerung zuerst greift), `chemenu.commands.doctor` (`check_tasks_provider`
meldet nur den konfigurierten Weg), `chemenu.review`/`chemenu.commands.review_cmd`
(`ReviewReport.source`, gerendert in Text und `--json`). Tests in `test_superproductivity.py`
(rundum neu, inkl. API-Stub-Server und Äquivalenztest), `test_new_page.py`, `test_review.py`,
`test_doctor.py`, `test_tasks_config.py`. Doku: `INSTALL.md` § Konfiguration,
`tools/CONTRACT.md` (Zeilen `review`, `new project`, `doctor`, Fehlervertrag `new project`),
Modul-Docstring von `superproductivity.py`. Version: `7.0.0-beta.10` → `7.0.0-beta.11` (MAJOR).
Commit `e07d1ca`.
torben
changed title from SP-Lesepfad zweistufig: erst die lokale API, dann der Backup-Schnappschuss to SP-Lesepfad je Instanz konfigurieren: API oder Schnappschuss, nicht beides2026-09-20 14:31:48 +00:00
Changelog: Von Laufzeit-Stufung mit Rückfall auf eine Konfigurationsangabe je Instanz
umgestellt (Betreiber, 2026-09-20: die Wege schließen sich je System aus — headless immer Datei,
Desktop immer API, kein Mischfall in Sicht). Titel entsprechend.
Entfallen: Bedingung B1 (Quelle einmal je Lauf pinnen) vollständig — ohne Laufzeitwahl kann
nichts mitten im Lauf kippen. Der Rückfall selbst ist gestrichen, samt der Frage, wann er greift.
Geblieben: der Äquivalenztest, jetzt mit dem Argument, dass die beiden Pfade in der Praxis
nie jemand nebeneinander sieht. Die Herkunftsangabe in kleinerer Form.
Neu: Konfigurationsform read_via: api|snapshot, fehlend = snapshot (drop-in); begründet
gegen api_base_url: null, weil sie später auto ohne Schema-Migration aufnehmen kann. Dazu die
Validierungsregel, dass die benannte Quelle konfiguriert sein muss.
Schärfer: ohne Rückfall muss der API-Pfad vollständig sein oder darf nicht angeboten werden.
Neuer Befund, ausgelagert:#134 — --resume verifiziert über den Lesepfad und sieht ein
frisch angelegtes Projekt womöglich nicht, weil der Schnappschuss älter ist. Das ist das stärkste
Einzelargument für den API-Pfad auf dem Desktop.
**Changelog:** Von Laufzeit-Stufung mit Rückfall auf eine Konfigurationsangabe je Instanz
umgestellt (Betreiber, 2026-09-20: die Wege schließen sich je System aus — headless immer Datei,
Desktop immer API, kein Mischfall in Sicht). Titel entsprechend.
- **Entfallen:** Bedingung B1 (Quelle einmal je Lauf pinnen) vollständig — ohne Laufzeitwahl kann
nichts mitten im Lauf kippen. Der Rückfall selbst ist gestrichen, samt der Frage, wann er greift.
- **Geblieben:** der Äquivalenztest, jetzt mit dem Argument, dass die beiden Pfade in der Praxis
nie jemand nebeneinander sieht. Die Herkunftsangabe in kleinerer Form.
- **Neu:** Konfigurationsform `read_via: api|snapshot`, fehlend = `snapshot` (drop-in); begründet
gegen `api_base_url: null`, weil sie später `auto` ohne Schema-Migration aufnehmen kann. Dazu die
Validierungsregel, dass die benannte Quelle konfiguriert sein muss.
- **Schärfer:** ohne Rückfall muss der API-Pfad vollständig sein oder darf nicht angeboten werden.
- **Neuer Befund, ausgelagert:** #134 — `--resume` verifiziert über den Lesepfad und sieht ein
frisch angelegtes Projekt womöglich nicht, weil der Schnappschuss älter ist. Das ist das stärkste
Einzelargument für den API-Pfad auf dem Desktop.
torben
changed title from SP-Lesepfad je Instanz konfigurieren: API oder Schnappschuss, nicht beides to SP-Zugriff je Instanz explizit: `access: api` oder `access: snapshot`, kein Default, kein Rückfall2026-09-20 14:43:03 +00:00
Changelog: Auf eine ausdrückliche, pflichtige Konfigurationsangabe umgestellt (Betreiber,
2026-09-20: keine Kompatibilität nötig, der Stack ist unveröffentlicht; keine Mischform;
Projektanlage nur per API). Titel entsprechend.
Feld:read_via mit Default snapshot → accessohne Default, Pflicht. Umbenannt, weil
es nicht mehr nur das Lesen entscheidet. Kein auto, kein Rückfall.
Neu: die Projektanlage gibt es nur bei access: api; eine snapshot-Instanz ist gegenüber
dem Tracker rein lesend. Dazu die Regel, dass der Konfigurationsblock nur die Felder seines Wegs
tragen darf.
Entfallen: die Rückwärtskompatibilität als Kriterium und das Argument für read_via gegen api_base_url: null (es stützte sich auf ein späteres auto, das es nicht geben wird).
Aufgelöst:#134 ist durch diese Entscheidung gegenstandslos und geschlossen — verify() läuft
nur noch dort, wo live gelesen wird. Die dort gefundene falsche Begründung in tasks/protocol.py ist als Akzeptanzkriterium hierher übernommen.
Neu offen: was new project auf einer snapshot-Instanz tut (Neigung: verweigern), und ob db_path neben backups_dir bestehen bleibt.
**Changelog:** Auf eine ausdrückliche, pflichtige Konfigurationsangabe umgestellt (Betreiber,
2026-09-20: keine Kompatibilität nötig, der Stack ist unveröffentlicht; keine Mischform;
Projektanlage nur per API). Titel entsprechend.
- **Feld:** `read_via` mit Default `snapshot` → `access` **ohne Default, Pflicht**. Umbenannt, weil
es nicht mehr nur das Lesen entscheidet. Kein `auto`, kein Rückfall.
- **Neu:** die Projektanlage gibt es nur bei `access: api`; eine `snapshot`-Instanz ist gegenüber
dem Tracker rein lesend. Dazu die Regel, dass der Konfigurationsblock nur die Felder seines Wegs
tragen darf.
- **Entfallen:** die Rückwärtskompatibilität als Kriterium und das Argument für `read_via` gegen
`api_base_url: null` (es stützte sich auf ein späteres `auto`, das es nicht geben wird).
- **Aufgelöst:** #134 ist durch diese Entscheidung gegenstandslos und geschlossen — `verify()` läuft
nur noch dort, wo live gelesen wird. Die dort gefundene falsche Begründung in
`tasks/protocol.py` ist als Akzeptanzkriterium hierher übernommen.
- **Neu offen:** was `new project` auf einer `snapshot`-Instanz tut (Neigung: verweigern), und ob
`db_path` neben `backups_dir` bestehen bleibt.
Changelog: Die beiden letzten Entwurfsfragen sind entschieden (Betreiber, 2026-09-20) und
stehen jetzt als Entscheidung im Body statt als Frage darunter. kind/decision → kind/build:
offen ist nur noch eine Verifikation gegen den SP-Quellcode, und die ist keine Betreiberfrage.
new project verweigert bei access: snapshot — vollständig, also auch ohne Seite, mit
Exit 1 statt 42. Dazu die Begründung, warum das neben „kein Tracker konfiguriert → nur die Seite"
konsistent ist: dort läuft review gar nicht, hier läuft es und meldet die halbe Paarung als
Befund von Prüfung 4.
db_path gestrichen — backups_dir ist das einzige Feld des snapshot-Wegs, latest_snapshot_path() verliert seinen Zweig. Das Gegenargument (Testbequemlichkeit) löst sich
auf: ein Verzeichnis mit genau einer Datei ist ebenso eindeutig wie ein Pfad darauf.
Zwei neue Kriterien dafür, plus eine Zeile in der Bump-Notiz (gestrichenes Feld zusätzlich
zum neuen Pflichtfeld).
**Changelog:** Die beiden letzten Entwurfsfragen sind entschieden (Betreiber, 2026-09-20) und
stehen jetzt als Entscheidung im Body statt als Frage darunter. `kind/decision` → `kind/build`:
offen ist nur noch eine Verifikation gegen den SP-Quellcode, und die ist keine Betreiberfrage.
- **`new project` verweigert bei `access: snapshot`** — vollständig, also auch ohne Seite, mit
Exit 1 statt 42. Dazu die Begründung, warum das neben „kein Tracker konfiguriert → nur die Seite"
konsistent ist: dort läuft `review` gar nicht, hier läuft es und meldet die halbe Paarung als
Befund von Prüfung 4.
- **`db_path` gestrichen** — `backups_dir` ist das einzige Feld des `snapshot`-Wegs,
`latest_snapshot_path()` verliert seinen Zweig. Das Gegenargument (Testbequemlichkeit) löst sich
auf: ein Verzeichnis mit genau einer Datei ist ebenso eindeutig wie ein Pfad darauf.
- **Zwei neue Kriterien** dafür, plus eine Zeile in der Bump-Notiz (gestrichenes Feld zusätzlich
zum neuen Pflichtfeld).
Changelog: Die Verifikation gegen den SP-Quellcode ist gelaufen (2026-09-20, super-productivity/super-productivity@master). Der API-Weg trägt den Lesepfad vollständig,
„Vorher zu klären" ist leer. Zwei Stellen des Bodys waren falsch und sind korrigiert.
Korrigiert: die Tabellenzelle „api schreibt: ja — Projektanlage". Es gibt weiterhin kein POST /projects; new project bleibt auch auf einer api-Instanz HumanInterventionRequired/Exit 42. Der Gewinn des API-Wegs dort ist allein die Live-Verifikation
durch --resume — also genau #134.
Korrigiert: die Pfadangabe für die Routen. Sie liegen im Renderer
(src/app/core/electron/local-rest-api-handler.service.ts), nicht in electron/…; die
Electron-Seite ist nur Transport, /health und Token-Prüfung. Der Modul-Docstring des Adapters
zieht nach.
Neu, Entscheidung:api_token ist bei access: api Pflichtfeld — alles außer GET /health
verlangt einen Bearer-Token.
Neu, Entscheidung (Betreiber): beide Wege filtern isArchived. GET /projects tut es über selectUnarchivedProjects, der Schnappschuss-Leser zog bisher nicht nach. Verhaltensänderung an
Prüfung 3, eigenes Kriterium plus Changelog-Zeile.
Neu: der API-Weg zählt offene Posten gegen project.taskIds, nicht gegen den projectId-Filter — sonst zählt er Unteraufgaben mit, die der Dateiweg nicht sieht.
Neu: das Streichen von db_path macht die Dateiwahl tragend, und die war zu weich. SP sortiert
selbst nach mtime, und die manuellen Exporte (sp-backup_* u. a.) sortieren hinter jeden
Zeitstempel. Der Glob wird auf YYYY-MM-DD_HHmmss.json eingeschränkt, mit eigenem Test.
Ausgelagert:#135 — follow_up_at liest mit remindAt das falsche SP-Feld (dueDay/ dueWithTime sind bei SP die Terminierung, deadline* das Fälligkeitsdatum). Prüfung 2 ist
dadurch heute blind für ganztägige Tickler, und der Schreibweg aus #132 wäre ohne die Korrektur
gar nicht möglich. Berührt dieses Issue nicht — hier wird der Weg gewählt, nicht das Feld.
**Changelog:** Die Verifikation gegen den SP-Quellcode ist gelaufen (2026-09-20,
`super-productivity/super-productivity@master`). Der API-Weg trägt den Lesepfad vollständig,
„Vorher zu klären" ist leer. Zwei Stellen des Bodys waren falsch und sind korrigiert.
- **Korrigiert:** die Tabellenzelle „`api` schreibt: ja — Projektanlage". Es gibt weiterhin kein
`POST /projects`; `new project` bleibt auch auf einer `api`-Instanz
`HumanInterventionRequired`/Exit 42. Der Gewinn des API-Wegs dort ist allein die Live-Verifikation
durch `--resume` — also genau #134.
- **Korrigiert:** die Pfadangabe für die Routen. Sie liegen im Renderer
(`src/app/core/electron/local-rest-api-handler.service.ts`), nicht in `electron/…`; die
Electron-Seite ist nur Transport, `/health` und Token-Prüfung. Der Modul-Docstring des Adapters
zieht nach.
- **Neu, Entscheidung:** `api_token` ist bei `access: api` Pflichtfeld — alles außer `GET /health`
verlangt einen Bearer-Token.
- **Neu, Entscheidung (Betreiber):** beide Wege filtern `isArchived`. `GET /projects` tut es über
`selectUnarchivedProjects`, der Schnappschuss-Leser zog bisher nicht nach. Verhaltensänderung an
Prüfung 3, eigenes Kriterium plus Changelog-Zeile.
- **Neu:** der API-Weg zählt offene Posten gegen `project.taskIds`, nicht gegen den
`projectId`-Filter — sonst zählt er Unteraufgaben mit, die der Dateiweg nicht sieht.
- **Neu:** das Streichen von `db_path` macht die Dateiwahl tragend, und die war zu weich. SP sortiert
selbst nach mtime, und die manuellen Exporte (`sp-backup_*` u. a.) sortieren hinter jeden
Zeitstempel. Der Glob wird auf `YYYY-MM-DD_HHmmss.json` eingeschränkt, mit eigenem Test.
- **Ausgelagert:** #135 — `follow_up_at` liest mit `remindAt` das falsche SP-Feld (`dueDay`/
`dueWithTime` sind bei SP die *Terminierung*, `deadline*` das Fälligkeitsdatum). Prüfung 2 ist
dadurch heute blind für ganztägige Tickler, und der Schreibweg aus #132 wäre ohne die Korrektur
gar nicht möglich. Berührt dieses Issue nicht — hier wird der Weg gewählt, nicht das Feld.
Umgesetzt und veröffentlicht: Commit e07d1ca, Stack 7.0.0-beta.10 → 7.0.0-beta.11 (MAJOR, --breaking/--no-migration). docs verify, instructions verify, pytest (1410 Tests) grün. Gemeinsam mit #135 in einem Publish umgesetzt. Body oben auf den Endzustand nachgezogen, alle Akzeptanzkriterien abgehakt.
Umgesetzt und veröffentlicht: Commit `e07d1ca`, Stack `7.0.0-beta.10` → `7.0.0-beta.11` (MAJOR, `--breaking`/`--no-migration`). `docs verify`, `instructions verify`, `pytest` (1410 Tests) grün. Gemeinsam mit #135 in einem Publish umgesetzt. Body oben auf den Endzustand nachgezogen, alle Akzeptanzkriterien abgehakt.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Umgesetzt und veröffentlicht (2026-09-20, Commit
e07d1ca, Stack7.0.0-beta.11, MAJOR mit--breaking/--no-migration):docs verify,instructions verifyund die vollepytest-Suite(1410 Tests) grün. Alle Akzeptanzkriterien unten sind erfüllt und abgehakt.
Der Super-Productivity-Adapter liest heute ausschließlich einen periodischen Backup-Schnappschuss.
Läuft die Anwendung, gibt es daneben eine lokale REST-API mit dem aktuellen Zustand.
Die Wege schließen sich je System aus (Betreiber, 2026-09-20): eine headless bediente Instanz
geht immer auf die Datei, der Desktop immer auf die API. Kein Mischfall in Sicht. Also sagt jede
Instanz ausdrücklich, welchen Weg sie nimmt — und nimmt nur den.
Kompatibilität ist kein Kriterium (Betreiber, 2026-09-20): der Stack ist unveröffentlicht, die
Konfiguration wird überarbeitet statt erweitert. Das Feld ist deshalb Pflicht und trägt keinen
Default — eine Konfiguration, die den Weg nicht nennt, ist unvollständig, nicht „die alte".
Alles entschieden, nichts mehr offen. Die Verifikation gegen den SP-Quellcode ist am
2026-09-20 gelaufen (Ergebnis unten); der API-Weg trägt den Lesepfad vollständig und darf
angeboten werden.
Die Entscheidung
accessim providereigenen Block, Pflicht, zwei Werte:apiapi_base_url,api_tokensnapshotbackups_dirKein
auto, kein Rückfall, keine Mischform. Der Name ist bewusst nichtread_via: das Feldentscheidet beide Hälften, nicht nur das Lesen.
Der Block trägt nur die Felder seines Wegs. Ein
backups_dirnebenaccess: apiwird beimLesen der Konfiguration abgelehnt, nicht ignoriert: eine Konfiguration, die zwei Wege beschreibt,
behauptet etwas, das nicht gilt — und beim nächsten Blick weiß niemand mehr, welcher Teil wirksam
ist.
api_tokenist beiaccess: apiPflicht, nicht das heute optionale Durchreichfeld:verifiziert ist, dass jeder Endpunkt außer
GET /healtheinenAuthorization: Bearer <token>verlangt. Eine
api-Instanz ohne Token kann nichts lesen.Projektanlage bleibt auch auf dem API-Weg ein Menschenschritt
Korrektur gegenüber der früheren Fassung dieses Bodys (Verifikation 2026-09-20): es gibt
weiterhin kein
POST /projects. Registriert sind ausschließlichGET /status,GET /focus,GET|POST /task-control/*,GET|POST /tasks,GET|PATCH|DELETE /tasks/:id,GET /projects,GET /tags(src/app/core/electron/local-rest-api-handler.service.ts:420). Projekt-CRUD undTag-CRUD existieren nicht.
wikitool new projectbleibt auf einerapi-Instanz also beiHumanInterventionRequired/Exit 42wie bisher. Was der API-Weg dort gewinnt, ist allein die Verifikation:
--resumeprüft übereinen Lesepfad, der den aktuellen Zustand sieht, statt gegen ein womöglich älteres Backup zu
laufen. Genau das war #134, und genau deshalb ist es aufgelöst.
new projectverweigert auf einersnapshot-InstanzEntschieden (Betreiber, 2026-09-20). Und zwar vollständig: kein Tracker-Projekt, und auch
keine Seite. Ein gewöhnlicher Validierungsfehler mit Exit 1, der auf die
api-Instanz verweist —kein Exit 42, denn hier wartet nichts auf eine Freigabe; das Kommando kann es an dieser Stelle
schlicht nicht.
--resumeverhält sich gleich.Das sieht neben dem vorhandenen Verhalten „kein Tracker konfiguriert → nur die Seite, ausdrücklich
angesagt" inkonsistent aus und ist es nicht. Der Unterschied ist, ob überhaupt gejoint wird:
reviewläuft gar nicht (es scheitert an der fehlendenKonfiguration). Eine Seite ohne Tracker-Projekt ist dort der Normalzustand und fällt niemandem
auf die Füße.
access: snapshot:reviewläuft. Eine Seite ohne Tracker-Projekt ist dort genau das, wasPrüfung 4 (
no_open_loop) als Befund meldet — die Hälfte eines Paars, angelegt von einer Instanz,die die andere Hälfte gar nicht anlegen kann.
Kosten: praktisch keine. An einer headless bedienten Instanz sitzt niemand, der ein Projekt anlegt.
db_pathwird gestrichenEntschieden (Betreiber, 2026-09-20).
backups_dirist damit füraccess: snapshotdas einzige undpflichtige Feld, und
latest_snapshot_path()verliert seinen Zweig.Was dieser Verzicht tragend macht (Verifikation 2026-09-20):
electron/backup.ts:148schreibt<userData>/backups/YYYY-MM-DD_HHmmss.json— ohne Präfix, lexikalisch also gleich chronologisch.Zwei Haken, die heute niemand bemerkt hätte, weil
db_pathsie umgehen konnte:listBackupFilesnach mtime, nicht lexikalisch. Der zitierte Kommentar„timestamps sort lexically" steht an der Windows-Store-Pfadwahl, nicht an der Sortierung.
sp-backup_*,sp-backup-anonymized_*,sp-pre-migration-backup_*(electron/shared-with-frontend/get-backup-timestamp.ts). Diesortieren hinter jeden reinen Zeitstempel. Eine einzige dorthin abgelegte Exportdatei würde
den Leser dauerhaft auf einen alten Stand nageln.
Also: der Glob wird auf das Zeitstempelmuster eingeschränkt statt auf
*.json.Das Ergebnis der Quellcode-Verifikation (2026-09-20)
Gegen
super-productivity/super-productivity@master. Die Routen liegen nicht inelectron/local-rest-api.ts— das ist nur Transport,/healthund Token-Prüfung — sondern imRenderer unter
src/app/core/electron/local-rest-api-handler.service.ts. Die Pfadangabe imModul-Docstring des Adapters ist entsprechend falsch und wird nachgezogen.
GET /projectsliefert die vollenProject-Objekte;created?: number(packages/plugin-api/src/types.ts)GET /tasksliefert volleTask-Objekte,?projectId=,includeDoneper DefaultfalseWAITING→ TagwaitingGET /tagsliefert volleTag-Objekte mittitle;task.tagIdsliegt am Taskfollow_up_atbacklogTaskIdsbacklogTaskIds: string[]am Project-Objektapi_tokenGET /healthverlangtAuthorization: BearerAlle drei Listen-Endpunkte geben die NgRx-Store-Objekte unverändert zurück, also strukturell
dieselben Datensätze, die auch im Schnappschuss stehen. Der Äquivalenztest hat es dadurch leicht —
mit genau zwei Ausnahmen, die er sonst als Fehlschlag meldet:
Divergenz 1: archivierte Projekte
GET /projectsläuft überselectUnarchivedProjectsund filtertisArchived(
src/app/features/project/store/project.selectors.ts:16). Der Schnappschuss-Leser iteriert heutealle
project-Entities. Prüfung 3 (unpaged_project_weeks) meldete je nach Weg verschieden viel.Entschieden (Betreiber, 2026-09-20): beide filtern
isArchived. Der Schnappschuss-Weg ziehtauf das API-Verhalten nach — ein archiviertes Projekt soll keinen
unpaged_project-Befunderzeugen. Das ist eine Verhaltensänderung an
reviewfür bestehende Schnappschuss-Instanzen(weniger Befunde) und braucht einen Changelog-Eintrag, der sie benennt.
Divergenz 2: Unteraufgaben
Unteraufgaben erben
projectIdvom Elternteil (addSubTaskerzwingt es),GET /tasks?projectId=liefert sie also mit;
project.taskIdsträgt nur die oberste Ebene. Der API-Weg zählt gegenproject.taskIdsstatt gegen denprojectId-Filter — dieselbe Wahrheitsquelle wie der Dateiweg.Was diese Form verschwinden lässt
Ein erster Entwurf hatte eine Laufzeit-Stufung mit Rückfall und drei Bedingungen daran. Zwei davon
entfallen vollständig:
run_reviewruft nacheinanderprojects(),open_items()je Projekt undsomeday_items(); mit einem Rückfall hätte ein Schließen derAnwendung dazwischen einen Bericht aus zwei Welten erzeugt — Befunde aus Prüfung 3/4, die echt
aussehen und keine sind. Ohne Laufzeitwahl ist das keine Bedingung mehr, sondern eine Eigenschaft
der Konstruktion.
new project --resumeprüft über den Lesepfad, obein Mensch das Projekt tatsächlich angelegt hat. Auf einer
snapshot-Instanz konnte diese Prüfunggegen ein Backup von vor dem Schritt des Menschen laufen und „immer noch nicht da" melden. Da es
die Anlage dort nicht mehr gibt, läuft
verify()nur noch, wo live gelesen wird. #134 ist damitaufgelöst und geschlossen; die dort benannte Korrektur an der Begründung in
tasks/protocol.pygehört in dieses Paket.
Was bleibt, ist der Äquivalenztest — und er wird dringlicher: wenn die beiden Wege auf
verschiedenen Maschinen leben, sieht sie in der Praxis nie jemand nebeneinander. Nur ein Test hält
sie zusammen. Dazu die Herkunftsangabe: die Antwort nennt ihren Weg, und bei
snapshotdessenAlter.
Was der Verzicht auf den Rückfall kostet — und warum er richtig ist
Ist die Anwendung auf einer
api-Instanz geschlossen, gibt es keine Antwort: Provider nichterreichbar, betroffene Prüfungen übersprungen, Bericht als
INCOMPLETEgekennzeichnet,reviewendet mit Exit 1. Das ist das vorhandene, ausdrücklich so gebaute Verhalten.
Und es ist die bessere Hälfte des Tauschs. Ein Rückfall hätte an derselben Stelle eine Antwort aus
einem zwei Tage alten Schnappschuss geliefert, die aussieht wie eine frische — genau die leise
Falschheit, gegen die der Stack sonst überall anläuft (
commit: null, derINCOMPLETE-Block, derCommit-Stempel an jeder MCP-Antwort). Die Abhilfe liegt beim Menschen und ist trivial: die Anwendung
öffnen.
Verifiziert dazu: die API kennt mit
503 APP_NOT_READYeinen eigenen Zustand für „Server da,Renderer noch nicht" — „nicht erreichbar" und „antwortet Unsinn" sind dort bereits getrennt.
Konfigurationsform
Berührungspunkte
access: snapshot. Dass sie den Tracker nichtschreiben kann, folgt damit aus der Konfiguration statt aus der Netztopologie — dieselbe
Eigenschaft, ausdrücklich statt beiläufig.
access: apivoraus. Ein Posten, der über die API angelegt wird,ist dort auch sofort wieder lesbar — das Kriterium „der Posten taucht anschließend in
wikitool reviewauf" ist damit sauber erfüllbar statt an ein Backup-Intervall gekoppelt.Verifiziert:
POST /tasksexistiert,tagIdsist schreibbar (derwaiting-Tag muss allerdingsschon existieren — Tags sind über die API nicht anlegbar),
isAddToBacklogist festfalse,ein Posten landet also nie im Backlog.
follow_up_atliest heuteremindAt, und das ist über die API nicht schreibbar. Erstmit der dortigen Korrektur auf
dueWithTime/dueDaywird #132s Schreibweg vollständig. Berührtdieses Issue nicht — hier wird nur der Weg gewählt, nicht das Feld. Umgesetzt zusammen mit
diesem Issue, im selben Publish.
TaskReadernichts an —keine Änderung an
tasks/protocol.pyaußer der Korrektur ihrer Begründung. Die Regel-1-Korrekturaus #135 wurde in #128s eigenem Body nachgezogen (dort, nicht hier, da sie den Begriff bindet, den
ein zweiter Adapter prüfen muss).
Akzeptanzkriterien
accessist Pflicht; eine Konfiguration ohne das Feld scheitert beim Lesen derKonfiguration mit einer Meldung, die beide Werte nennt — nicht erst beim ersten Zugriff.
access: apiliest ausschließlich über die API,access: snapshotausschließlich aus derDatei. Kein Codepfad wechselt den Weg, unter keinem Fehlerzustand.
api_tokenist beiaccess: apiPflicht; eine Konfiguration ohne Token scheitert beim Lesender Konfiguration, nicht erst am ersten
401.db_pathexistiert nicht mehr — weder im Code, noch in_EXPECTED_PROVIDER_CONFIG, noch ineinem Test oder einer Dokumentationsstelle. Die Fixtures pinnen stattdessen ein Verzeichnis
mit genau einer Datei.
latest_snapshot_path()wählt nur Dateien, deren Name dem MusterYYYY-MM-DD_HHmmss.jsonentspricht. Ein Test legt eine
sp-backup_*.jsonneben eine neuere Zeitstempeldatei undprüft, dass die Zeitstempeldatei gewinnt.
wikitool new projectlegt beiaccess: snapshotnichts an — kein Tracker-Projekt undkeine Seite — und endet mit Exit 1 und einer Meldung, die auf die
api-Instanz verweist.--resumeebenso. Ein Test prüft, dass nach dem Aufruf keine Datei entstanden ist.access: apiverhält sichnew projectunverändert:HumanInterventionRequired/Exit 42,und
--resumeverifiziert über den Live-Lesepfad. Ein Test hält fest, dass dieser Weg keinPOST /projectsversucht.ProjectSummary/OpenItems/SomedayItem-Werte; kein Test braucht eine laufende Super-Productivity-Instanz — derAPI-Weg wird gegen eine Attrappe geprüft.
isArchived: trueaus. Ein Test hält das für beide fest,und der Changelog-Eintrag benennt die Verhaltensänderung an Prüfung 3.
project.taskIds, nicht gegen denprojectId-Filter;eine Fixture mit Unteraufgaben zeigt, dass beide Wege dieselbe Zahl liefern.
snapshotdessen Alter;wikitool reviewzeigt es in beiden Ausgabeformen.Schnappschuss-Form — „nicht erreichbar" und „antwortet Unsinn" bleiben verschiedene Zustände,
und keiner von beiden wechselt den Weg.
tasks/protocol.py(„re-reads on every call, so a caller checkingverify()... sees the current state") sagt, was tatsächlich gilt — die aus #134 übernommeneKorrektur.
wikitool doctormeldet den konfigurierten Weg und dessen Zustand; der andere ist keinBefund. Weiterhin nie ein
FAIL— eine geschlossene Anwendung ist kein Fehler.superproductivity.py,tools/CONTRACT.md(
doctor-Zeile, der Fehlervertrag zureview, dienew project-Zeile samt ihrem neuenExit-1-Fall),
INSTALL.md§ Konfiguration.docs verify,instructions verify,pytestgrün. Version gebumpt — MAJOR, mit--breaking(das neue Pflichtfeld und das gestrichenedb_path) und--no-migration(keine
kb/-Inhaltsänderung; der Bruch bleibt auf.wikitool-tasks.jsonbeschränkt).Vorher zu klären
Nichts mehr. Die einzige offene Frage — ob die API alle Lesefragen beantwortet — ist am 2026-09-20
gegen den Quellcode verifiziert; das Ergebnis steht oben.
Entschieden (2026-09-20):
accessstattread_via, Pflicht ohne Default; kein Rückfall, keinauto; Schreibweg nur beiaccess: api;new projectverweigert aufsnapshotvollständig undbleibt auf
apiein verifizierter Menschenschritt;db_pathgestrichen und der Glob dafürgehärtet;
api_tokenbeiaccess: apiPflicht; beide Wege filternisArchived.Abhängigkeiten
Keine blockierenden. Berührungspunkte: #132 (der Schreibweg), #135 (das Feld hinter
follow_up_at— umgesetzt im selben Publish), #131 (die headless bediente Instanz), #128 (nichtberührt, außer der nachgezogenen Regel-1-Korrektur). #134 ist durch dieses Issue aufgelöst und
geschlossen.
Umsetzung
chemenu.tasks.superproductivity:SuperProductivityConfig.access(Pflichtfeld, zwei Werte, jeeigene Feldmenge),
SuperProductivityReader(Schnappschuss, gehärteter Glob,isArchived-Filter),neu
SuperProductivityApiReader(REST-API, gegen eine Attrappe getestet),chemenu.tasks.protocol(neu
ReadSource,TaskReader.source()),chemenu.tasks.build_reader/build_writer(Dispatchnach
access,build_writerverweigert aufsnapshot),chemenu.commands.new_page(
_ensure_tracker_projectbaut den Writer vor dem Kollisions-Check, damit dieaccess: snapshot-Verweigerung zuerst greift),chemenu.commands.doctor(check_tasks_providermeldet nur den konfigurierten Weg),
chemenu.review/chemenu.commands.review_cmd(
ReviewReport.source, gerendert in Text und--json). Tests intest_superproductivity.py(rundum neu, inkl. API-Stub-Server und Äquivalenztest),
test_new_page.py,test_review.py,test_doctor.py,test_tasks_config.py. Doku:INSTALL.md§ Konfiguration,tools/CONTRACT.md(Zeilenreview,new project,doctor, Fehlervertragnew project),Modul-Docstring von
superproductivity.py. Version:7.0.0-beta.10→7.0.0-beta.11(MAJOR).Commit
e07d1ca.SP-Lesepfad zweistufig: erst die lokale API, dann der Backup-Schnappschussto SP-Lesepfad je Instanz konfigurieren: API oder Schnappschuss, nicht beidesChangelog: Von Laufzeit-Stufung mit Rückfall auf eine Konfigurationsangabe je Instanz
umgestellt (Betreiber, 2026-09-20: die Wege schließen sich je System aus — headless immer Datei,
Desktop immer API, kein Mischfall in Sicht). Titel entsprechend.
nichts mitten im Lauf kippen. Der Rückfall selbst ist gestrichen, samt der Frage, wann er greift.
nie jemand nebeneinander sieht. Die Herkunftsangabe in kleinerer Form.
read_via: api|snapshot, fehlend =snapshot(drop-in); begründetgegen
api_base_url: null, weil sie späterautoohne Schema-Migration aufnehmen kann. Dazu dieValidierungsregel, dass die benannte Quelle konfiguriert sein muss.
--resumeverifiziert über den Lesepfad und sieht einfrisch angelegtes Projekt womöglich nicht, weil der Schnappschuss älter ist. Das ist das stärkste
Einzelargument für den API-Pfad auf dem Desktop.
SP-Lesepfad je Instanz konfigurieren: API oder Schnappschuss, nicht beidesto SP-Zugriff je Instanz explizit: `access: api` oder `access: snapshot`, kein Default, kein RückfallChangelog: Auf eine ausdrückliche, pflichtige Konfigurationsangabe umgestellt (Betreiber,
2026-09-20: keine Kompatibilität nötig, der Stack ist unveröffentlicht; keine Mischform;
Projektanlage nur per API). Titel entsprechend.
read_viamit Defaultsnapshot→accessohne Default, Pflicht. Umbenannt, weiles nicht mehr nur das Lesen entscheidet. Kein
auto, kein Rückfall.access: api; einesnapshot-Instanz ist gegenüberdem Tracker rein lesend. Dazu die Regel, dass der Konfigurationsblock nur die Felder seines Wegs
tragen darf.
read_viagegenapi_base_url: null(es stützte sich auf ein späteresauto, das es nicht geben wird).verify()läuftnur noch dort, wo live gelesen wird. Die dort gefundene falsche Begründung in
tasks/protocol.pyist als Akzeptanzkriterium hierher übernommen.new projectauf einersnapshot-Instanz tut (Neigung: verweigern), und obdb_pathnebenbackups_dirbestehen bleibt.Changelog: Die beiden letzten Entwurfsfragen sind entschieden (Betreiber, 2026-09-20) und
stehen jetzt als Entscheidung im Body statt als Frage darunter.
kind/decision→kind/build:offen ist nur noch eine Verifikation gegen den SP-Quellcode, und die ist keine Betreiberfrage.
new projectverweigert beiaccess: snapshot— vollständig, also auch ohne Seite, mitExit 1 statt 42. Dazu die Begründung, warum das neben „kein Tracker konfiguriert → nur die Seite"
konsistent ist: dort läuft
reviewgar nicht, hier läuft es und meldet die halbe Paarung alsBefund von Prüfung 4.
db_pathgestrichen —backups_dirist das einzige Feld dessnapshot-Wegs,latest_snapshot_path()verliert seinen Zweig. Das Gegenargument (Testbequemlichkeit) löst sichauf: ein Verzeichnis mit genau einer Datei ist ebenso eindeutig wie ein Pfad darauf.
zum neuen Pflichtfeld).
Changelog: Die Verifikation gegen den SP-Quellcode ist gelaufen (2026-09-20,
super-productivity/super-productivity@master). Der API-Weg trägt den Lesepfad vollständig,„Vorher zu klären" ist leer. Zwei Stellen des Bodys waren falsch und sind korrigiert.
apischreibt: ja — Projektanlage". Es gibt weiterhin keinPOST /projects;new projectbleibt auch auf einerapi-InstanzHumanInterventionRequired/Exit 42. Der Gewinn des API-Wegs dort ist allein die Live-Verifikationdurch
--resume— also genau #134.(
src/app/core/electron/local-rest-api-handler.service.ts), nicht inelectron/…; dieElectron-Seite ist nur Transport,
/healthund Token-Prüfung. Der Modul-Docstring des Adapterszieht nach.
api_tokenist beiaccess: apiPflichtfeld — alles außerGET /healthverlangt einen Bearer-Token.
isArchived.GET /projectstut es überselectUnarchivedProjects, der Schnappschuss-Leser zog bisher nicht nach. Verhaltensänderung anPrüfung 3, eigenes Kriterium plus Changelog-Zeile.
project.taskIds, nicht gegen denprojectId-Filter — sonst zählt er Unteraufgaben mit, die der Dateiweg nicht sieht.db_pathmacht die Dateiwahl tragend, und die war zu weich. SP sortiertselbst nach mtime, und die manuellen Exporte (
sp-backup_*u. a.) sortieren hinter jedenZeitstempel. Der Glob wird auf
YYYY-MM-DD_HHmmss.jsoneingeschränkt, mit eigenem Test.follow_up_atliest mitremindAtdas falsche SP-Feld (dueDay/dueWithTimesind bei SP die Terminierung,deadline*das Fälligkeitsdatum). Prüfung 2 istdadurch heute blind für ganztägige Tickler, und der Schreibweg aus #132 wäre ohne die Korrektur
gar nicht möglich. Berührt dieses Issue nicht — hier wird der Weg gewählt, nicht das Feld.
Umgesetzt und veröffentlicht: Commit
e07d1ca, Stack7.0.0-beta.10→7.0.0-beta.11(MAJOR,--breaking/--no-migration).docs verify,instructions verify,pytest(1410 Tests) grün. Gemeinsam mit #135 in einem Publish umgesetzt. Body oben auf den Endzustand nachgezogen, alle Akzeptanzkriterien abgehakt.