Index: src/services/ActivityPubService.js
===================================================================
--- src/services/ActivityPubService.js	(revision 54b027a0ddd850a81ee455880a44caa957a477cc)
+++ src/services/ActivityPubService.js	(revision 44dfbd284ec37aa6fe63e8f8ad80a349ae80dff9)
@@ -5783,4 +5783,35 @@
     return 202; // decline to act; no 4xx, the sender may be a well-meaning retrying server
   }
+  // EERST de guardianship, DAARNA pas de follows. Die volgorde is geen netheid
+  // maar de hele werking, en hij is met bloed geschreven: bij Robins verhuizing
+  // op 13-8 stond het andersom en het log liet precies zien wat er dan gebeurt.
+  //
+  //   [AP] outgoing Follow beta → .../robo (gated, awaiting guardians)
+  //
+  // Beta is zelf een ward. Zijn UITGAANDE follow naar de verhuisde guardian werd
+  // gepoort (§5.3), want op dat moment stond het nieuwe adres nog niet in zijn
+  // guardian-lijst: de code hieronder had de relatie nog niet bijgewerkt. En de
+  // INKOMENDE kant heeft hetzelfde probleem, want de ward gate't een Follow van
+  // een onbekende. Dus beide richtingen bleven hangen op goedkeuring die niemand
+  // hoefde te geven, omdat het om een guardian ging die er al was.
+  //
+  // Met de relatie eerst is de verhuisde actor al een erkende guardian als de
+  // follows langskomen, en gaat de auto-acceptatie gewoon door.
+  //
+  // Een Move is een Move: de guardian is dezelfde guardian, het kind is hetzelfde
+  // kind, alleen het adres is nieuw. Zelfde bescherming als de re-follow: alleen
+  // na een geverifieerde Move, en niet naar een geblokkeerde bestemming (daar
+  // zijn we hierboven al uitgestapt). De twee harde randen van shaer-tge staan
+  // hier LOS van: weigeren te verhuizen naar een instance die shaer:guardians
+  // niet kan dragen is een controle aan de UITGAANDE kant, en het
+  // terugkeren-zonder-set is een alsoKnownAs-kwestie.
+  try {
+    const g = db.prepare('SELECT slug, role FROM ap_guardianships WHERE other_uri = ? AND status = ?').all(oldUri, 'accepted');
+    if (g.length) {
+      const r = db.prepare('UPDATE ap_guardianships SET other_uri = ? WHERE other_uri = ? AND status = ?').run(newUri, oldUri, 'accepted');
+      console.log('[AP] guardianship moved:', oldUri, '→', newUri, `(${r.changes}x)`, g.map((x) => `${x.role}:${x.slug}`).join(', '));
+    }
+  } catch (e) { console.warn('[AP] guardianship move failed:', e && e.message); }
+
   for (const row of rows) {
     const site = db.prepare('SELECT * FROM sites WHERE slug = ?').get(row.slug);
@@ -5795,24 +5826,4 @@
     }
   }
-  // Een Move is een Move: de guardian is dezelfde guardian, het kind is hetzelfde
-  // kind, alleen het adres is nieuw. Toezicht IS een gewone Follow (FEP-633c §5),
-  // en die is hierboven al meeverhuisd -- maar de relatie zelf hing nog aan de
-  // oude URI. Dat leverde de vervelendste toestand op: de guardian ziet de posts
-  // nog binnenkomen, terwijl alles dat op other_uri matcht omvalt (de gate op een
-  // nieuwe volger, het escalatiepad, listGuardians bij het kind). Half een
-  // vangnet ziet eruit als een heel vangnet.
-  //
-  // Zelfde bescherming als de re-follow hierboven: alleen na een geverifieerde
-  // Move, en niet als de bestemming geblokkeerd is (daar zijn we al uitgestapt).
-  // De twee harde randen van shaer-tge staan hier LOS van: weigeren te verhuizen
-  // naar een instance die shaer:guardians niet kan dragen is een controle aan de
-  // UITGAANDE kant, en het terugkeren-zonder-set is een alsoKnownAs-kwestie.
-  try {
-    const g = db.prepare('SELECT slug, role FROM ap_guardianships WHERE other_uri = ? AND status = ?').all(oldUri, 'accepted');
-    if (g.length) {
-      const r = db.prepare('UPDATE ap_guardianships SET other_uri = ? WHERE other_uri = ? AND status = ?').run(newUri, oldUri, 'accepted');
-      console.log('[AP] guardianship moved:', oldUri, '→', newUri, `(${r.changes}x)`, g.map((x) => `${x.role}:${x.slug}`).join(', '));
-    }
-  } catch (e) { console.warn('[AP] guardianship move failed:', e && e.message); }
   return 202;
 }
Index: test/move-guardianship.test.js
===================================================================
--- test/move-guardianship.test.js	(revision 54b027a0ddd850a81ee455880a44caa957a477cc)
+++ test/move-guardianship.test.js	(revision 44dfbd284ec37aa6fe63e8f8ad80a349ae80dff9)
@@ -92,4 +92,30 @@
 });
 
+// De VOLGORDE, niet de uitkomst. De tests hierboven slagen ook als de relatie pas
+// na de follows wordt bijgewerkt, en juist dat ging mis bij Robins verhuizing:
+//
+//   [AP] outgoing Follow beta → .../robo (gated, awaiting guardians)
+//
+// Beta is zelf een ward, dus zijn uitgaande follow naar de verhuisde guardian werd
+// gepoort omdat het nieuwe adres nog niet in zijn guardian-lijst stond. Beide
+// richtingen bleven hangen op een goedkeuring die niemand hoefde te geven.
+test('de guardianship is al bijgewerkt VOORDAT de follow uitgaat', async () => {
+  let standTijdensFollow = null;
+  const stil = console.log; console.log = () => {};
+  try {
+    await AP.handleMoveInbox(move, {
+      ...stubs,
+      followFn: async () => {
+        // Op dit moment moet de gate aan de andere kant ons al kennen.
+        standTijdensFollow = db.prepare('SELECT other_uri FROM ap_guardianships').get().other_uri;
+        return true;
+      },
+    });
+  } finally { console.log = stil; }
+
+  assert.equal(standTijdensFollow, NIEUW,
+    'staat de relatie hier nog op het oude adres, dan gate\'t de ward de follow van zijn eigen guardian');
+});
+
 test('een Move naar een geblokkeerde bestemming verandert niets', async () => {
   db.prepare('INSERT INTO ap_blocks (slug, target, kind) VALUES (?,?,?)').run('voogd', NIEUW, 'actor');
