Index: src/services/ArchiveExportService.js
===================================================================
--- src/services/ArchiveExportService.js	(revision 58cfe5f79a0c04fbb8d1908fcd73af6c8e0b3c85)
+++ src/services/ArchiveExportService.js	(revision 812fea90d4ea584a6d5809ffc45c9d527d45b7e7)
@@ -81,20 +81,25 @@
   const uit = [];
   const zie = new Set();
-  const voegToe = (url, name) => {
+  const voegToe = (url, name, rol, extra = {}) => {
     const u = String(url || '').trim();
     if (!u || zie.has(u)) return;
     zie.add(u);
-    uit.push({ url: u, name: name || null });
+    uit.push({ url: u, name: name || null, rol, ...extra });
   };
-  voegToe(post.cover_image_url, post.cover_alt);
-  voegToe(post.cover_video_url, post.cover_alt);
-  for (const m of String(post.content || '').matchAll(/<img[^>]+src=["']([^"']+)["'][^>]*>/gi)) voegToe(m[1]);
+  // De ROL is niet decoratief. Zonder rol staat er in het archief wel een
+  // bestand, maar niet dat het de cover was of bij de speler hoorde -- en dan
+  // komt de post na een herstel zonder cover en zonder speler terug. Gevonden
+  // door bij de oefenherstel ALLE kolommen te vergelijken in plaats van een
+  // handjevol.
+  voegToe(post.cover_image_url, post.cover_alt, 'cover');
+  voegToe(post.cover_video_url, post.cover_alt, 'coverVideo');
+  for (const m of String(post.content || '').matchAll(/<img[^>]+src=["']([^"']+)["'][^>]*>/gi)) voegToe(m[1], null, 'inline');
   try {
     for (const a of JSON.parse(post.c2s_attachments || '[]')) {
-      voegToe(a && a.url, a && a.name);
+      voegToe(a && a.url, a && a.name, 'c2s');
       // Een audio-bijlage draagt een poster (de omslag die de speler toont). Die
       // staat in een eigen veld en zou anders stil wegvallen -- op beta viel dat
       // pas op bij de export van echte data.
-      voegToe(a && a.poster, a && a.name ? `${a.name} (poster)` : null);
+      voegToe(a && a.poster, a && a.name ? `${a.name} (poster)` : null, 'poster', { posterFor: a && a.url });
     }
   } catch { /* kapotte kolom blokkeert de export niet */ }
@@ -103,5 +108,5 @@
     try {
       const t = db.prepare('SELECT t.title, m.storage_path FROM audio_tracks t LEFT JOIN media m ON m.id = t.media_id WHERE t.id = ?').get(m[1]);
-      if (t && t.storage_path) voegToe(`/media/${path.relative(path.resolve(MEDIA_ROOT), path.resolve(t.storage_path))}`, t.title);
+      if (t && t.storage_path) voegToe(`/media/${path.relative(path.resolve(MEDIA_ROOT), path.resolve(t.storage_path))}`, t.title, 'track');
     } catch { /* geen audio-tabellen: niets te doen */ }
   }
@@ -251,4 +256,6 @@
           'shaer:originalUrl': /^https?:/i.test(ref.url) ? ref.url : `${origin}${ref.url}`,
           'shaer:sha256': hash,
+          'shaer:role': ref.rol,
+          'shaer:posterFor': ref.posterFor || undefined,
         });
       } else {
@@ -261,4 +268,6 @@
           type: as2TypeOf(mime), mediaType: mime, name: ref.name || undefined,
           url: orig, 'shaer:availability': 'missing', 'shaer:originalUrl': orig,
+          'shaer:role': ref.rol,
+          'shaer:posterFor': ref.posterFor || undefined,
         });
       }
