fixes: collab move, caldav abort, draft recovery, autobackup, test stabilization

- Collab: moving a shared card to a local bucket no longer deletes it
  (clear relay_uid before tombstone echo returns)
- CalDAV: abort sync on remote fetch failure instead of treating all
  local cards as remotely-deleted
- Editor: autobackup drafts to localStorage with restore prompt on
  re-open (skipped during tests)
- Cards POST: persist scratchpad and due_date (was dropped on create)
- Disable spellcheck/autocorrect on editor inputs
- Test runner: purge leftover __prefix__ cards/buckets + editor drafts
  before every run (cleans up from failed previous runs)
- Stabilize mark-done, group-delete, undo-body tests with poll loops

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
thandClaude Opus 4.6 committed 2026-04-14 15:01:07 +02:00
1 parent 7e2b130434
commit 3cca14a3c6
8 files changed
+178 -23

No files matched your search

+13
View File
@@ -381,11 +381,16 @@ class CalDavSync
$remoteMap = []; // uid -> event data (gets modified during reconciliation)
$remoteAll = []; // uid -> event data (unmodified copy for tombstone lookups)
$remoteEvents = [];
$fetchFailed = false;
foreach ($urls['all'] as $calUrl) {
$calSlug = basename(rtrim($calUrl, '/'));
$cal = $this->makeClient($calUrl);
$res = $cal->request('REPORT', $calUrl, '<?xml version="1.0"?><c:calendar-query xmlns:d="DAV:" xmlns:c="urn:ietf:params:xml:ns:caldav"><d:prop><d:getetag/><c:calendar-data/></d:prop><c:filter><c:comp-filter name="VCALENDAR"><c:comp-filter name="VEVENT"/></c:comp-filter></c:filter></c:calendar-query>', ['Depth: 1']);
$this->addLog('sync', null, null, 'fetch', $res['status'] == 207 ? 'ok' : 'error', "REPORT {$calSlug} HTTP {$res['status']}");
if ($res['status'] != 207) {
$fetchFailed = true;
continue;
}
$events = $cal->listEvents();
$this->addLog('sync', null, null, 'fetch', 'ok', count($events) . " events from {$calSlug}");
$parseFail = 0;
@@ -403,6 +408,14 @@ class CalDavSync
if ($parseFail) $this->addLog('sync', null, null, 'fetch', 'error', "{$parseFail} events failed to parse from {$calSlug}");
}
// Abort on fetch failure — an incomplete remote list would make every
// local card look remotely-deleted and wipe them all. Better to sync
// nothing than to destroy local data on a flaky connection.
if ($fetchFailed) {
$this->addLog('sync', null, null, 'abort', 'error', 'remote fetch failed — skipping reconciliation to protect local data');
return $this->stats;
}
$this->addLog('sync', null, null, 'fetch', 'ok', "total remote: " . count($remoteMap) . " events");
// 3. Fetch local appointment cards — only #termin tagged or already synced