feat(tasks): support reminder write via dot-notation (v1.2.0)

- Add reminder_unit, reminder_value to create_task
- Add reminder_unit, reminder_value, clear_reminder to update_task
- Wire format verified: reminder.reminderUnit / .reminderValue /
  .reminderType (SNOOZE=active, NONE=clear) / .localId (optional)
- Valid units: MINUTE, HOUR, DAY (WEEK rejected by enum decoder)
- Clear requires full inactive block; partial updates reject with
  "task reminder invalid"
- Flat top-level keys, JSON string and bracket notation are silently
  ignored by the server — confirmed via isolated fuzz per variant
- Update SPEC.md, CLAUDE.md, README.md, CHANGELOG.md
- Remove outdated "not supported" warning in update_task docstring
This commit is contained in:
2026-04-17 22:41:16 +02:00
parent d6d8d40305
commit 4c1e4e2c23
6 changed files with 147 additions and 22 deletions
+22 -9
View File
@@ -213,15 +213,28 @@ POST https://api.familywall.com/api/taskupdate2
**⚠️ Encoding:** Der FiZ-`Ai()`-Encoder serialisiert alle Felder **flach** als Top-Level-Form-Parameter.
Recurrency-Felder werden direkt auf Top-Level gesendet (verifiziert via xb-Encoder im JS-Bundle).
**⚠️ Reminder-Schreibzugriff nicht möglich:**
Alle Versuche, `reminderUnit`, `reminderValue`, `reminderType`, `localId` via `taskupdate2`
zu setzen, wurden ignoriert — die API antwortet mit HTTP 200 und `lastAction: UPDATED`,
aber `reminder` bleibt unverändert auf dem Default-Wert `{reminderUnit: MINUTE, reminderType: NONE, reminderValue: 0}`.
Auch alternative Encodings (JSON-String, PHP-Bracket, verschachtelt) und alternative Endpoints
(`tasksetalert`, `taskalertput` etc.) wurden erprobt — alle Endpoints sind nicht registriert.
**Ursache:** Reminder-Updates werden vom Service Worker der mobilen App abgefangen und transformiert.
Diese Transformation ist nicht reproduzierbar ohne den Service Worker.
Reminder sind daher **read-only** (wie in v0.9 dokumentiert).
**Reminder-Schreibzugriff (verifiziert 2026-04-17):**
Reminder werden via Dot-Notation als Unterfelder gesendet:
| Parameter | Wert |
|---|---|
| `reminder.reminderUnit` | `"MINUTE"` \| `"HOUR"` \| `"DAY"` (WEEK wird abgelehnt) |
| `reminder.reminderValue` | nicht-negativer Integer als String (z.B. `"30"`) |
| `reminder.reminderType` | `"SNOOZE"` (aktiv) oder `"NONE"` (Reminder entfernen) |
| `reminder.localId` | optional; `"0"` akzeptiert |
**Wichtig:**
- Nur Dot-Notation funktioniert. Flache Top-Level-Keys (`reminderUnit`, `reminderValue`, …),
JSON-String (`reminder={…}`) und Bracket-Notation (`reminder[reminderUnit]`) werden
**silent-ignored** (HTTP 200, Reminder unverändert).
- Partielle Reminder-Updates (z.B. nur `reminder.reminderType=NONE`) liefern
`task reminder invalid` — immer vollständigen Block senden.
- `reminder=$empty` wird silent-ignored; zum Entfernen muss `reminder.reminderType=NONE`
mit `reminder.reminderValue=0` und `reminder.reminderUnit=MINUTE` gesendet werden.
- Reminder-Felder lassen sich in einem einzigen `taskupdate2`-Call gemeinsam mit
`recurrency`/`dueDate`/`text` setzen — keine Interferenz.
- `$empty` auf Unterfelder (`reminder.reminderValue=$empty`) führt zu Parse-Fehlern.
**Response:**
```