Index: src/services/ActivityPubService.js
===================================================================
--- src/services/ActivityPubService.js	(revision d1075a16677d12c60b0d792c821a5baea451185d)
+++ src/services/ActivityPubService.js	(revision 3a0ca0f34e3f112899e4fdc916ec87010867dc3e)
@@ -5422,7 +5422,22 @@
 
 // Follow a fediverse account by @handle (WebFinger → actor → signed Follow).
-export async function followActor(site, handle, autoBoost = false) {
+export async function followActor(site, handle, autoBoost = false, { approved = false } = {}) {
   const base = (process.env.PUBLIC_BASE_URL || '').replace(/\/+$/, '');
   if (!base || !site || !site.slug) return { error: 'config' };
+  // DE POORT STAAT HIER, en niet alleen in de C2S-outbox (shaer-p729, Barts
+  // melding 8-8: de volgverzoeken van Esmee kwamen nooit bij haar guardians
+  // aan). Hij stond in `case 'Follow'` van de outbox -- dus alleen als je via
+  // Shaer volgt. Volgde het kind vanuit Klonkts eigen webinterface, dan werd er
+  // geen verzoek aangemaakt, ging er niets naar de guardians, en was er dus ook
+  // niets om te beantwoorden. Precies dezelfde deur-naast-de-poort als bij de
+  // antwoordpoort vanmiddag (shaer-r4c).
+  //
+  // Merk op wat het NIET was: niet dat een guardian elders het niet kon
+  // beantwoorden. Die weg werkt en levert een Offer af bij de externe guardian.
+  // Er kwam alleen nooit iets aan om af te leveren.
+  //
+  // `approved` is de enige doorlaat, voor performApprovedFollow: zonder dat zou
+  // een goedgekeurd verzoek opnieuw op de poort stuiten en voor eeuwig wachten.
+
   // Accept any of: a profile/actor URL, an @user@host handle (WebFinger), or a
   // bare site domain (site.com) — for a single-actor site (Klonkt etc.) the root
@@ -5435,4 +5450,12 @@
   else actorUrl = null;
   if (!actorUrl) return { error: 'not_found' };
+  // NA het oplossen, want een kind volgt net zo goed met @naam@server of een
+  // kaal domein. Zou de poort alleen naar de ruwe invoer kijken, dan is elke
+  // handle een sluiproute -- en dat is precies de fout die we hier repareren,
+  // een maat kleiner.
+  if (!approved) {
+    const held = await gateOutgoingFollow(site, actorUrl);
+    if (held) return { held: true, id: held.id, status: held.status || 'pending' };
+  }
   // SIGNED, as this actor: an authorized-fetch instance refuses an anonymous
   // GET of the actor doc, which made following from a boost silently fail
@@ -5759,5 +5782,5 @@
   const site = db.prepare('SELECT * FROM sites WHERE slug = ?').get(pending.ward_slug);
   if (!site) return { error: 'no_such_ward' };
-  const r = await followActor(site, pending.target_uri);
+  const r = await followActor(site, pending.target_uri, false, { approved: true });
   if (r && r.error) return { error: r.error };
   console.log('[AP] outgoing Follow approved', pending.ward_slug, '→', pending.target_uri);
Index: src/services/guardianship/follows.js
===================================================================
--- src/services/guardianship/follows.js	(revision d1075a16677d12c60b0d792c821a5baea451185d)
+++ src/services/guardianship/follows.js	(revision 3a0ca0f34e3f112899e4fdc916ec87010867dc3e)
@@ -50,4 +50,33 @@
  * send the Accept/Reject. A single reject denies; approvals meet the quorum.
  */
+/**
+ * Hoeveel guardians moeten ja zeggen voor een volgverzoek (Barts besluit, 8-8).
+ *
+ * EENVOUDIGE MEERDERHEID: 1 van 1, 1 van 2, 2 van 3, 2 van 4. Bart: "1/2 is
+ * voldoende."
+ *
+ * BEWUST SOEPELER DAN DE POORTDREMPEL, en dat verschil hoort uitgelegd. Een gate
+ * opent een deur voor alles wat daarna komt; die vraagt om een STRIKTE
+ * meerderheid (thresholdFor in gated.js: 2 van 2, 3 van 4). Een volgverzoek gaat
+ * over een persoon, is met ontvolgen terug te draaien, en stond hier tot vandaag
+ * op 'any' -- een enkele ja, hoeveel guardians er ook waren. Dit is dus geen
+ * versoepeling maar een AANSCHERPING voor iedereen met drie of meer guardians.
+ *
+ * De 'all'-stand die hier stond is weg. Hij werd nergens gezet -- elke schrijver
+ * gaf 'any' mee -- dus het was een keuze die niemand kon maken en die alleen in
+ * de weg stond bij het lezen van deze regel.
+ */
+export function followThreshold(setSize) {
+  return Math.max(1, Math.ceil(setSize / 2));
+}
+
+/**
+ * Een race naar de drempel, net als de poorttelling: zodra het aantal gehaald
+ * is, is het besluit gevallen.
+ *
+ * TODO (shaer-8vt): wie antwoordt weet niet dat hij de doorslag geeft. Bij 1 van
+ * 2 is de eerste ja meteen de beslissing, en het scherm zegt dat nergens. Dat is
+ * hetzelfde gat als bij de gate-voorstellen en het hoort daar samen opgelost.
+ */
 export function decide(id, guardianUri, decision, guardiansOfWard) {
   const follow = stmts().get.get(id);
@@ -61,7 +90,5 @@
   const approvers = new Set(rows.filter((r) => r.decision === 'approve').map((r) => r.guardian_uri));
   const guardians = (guardiansOfWard || []).filter(Boolean);
-  const enough = follow.quorum === 'all'
-    ? guardians.length > 0 && guardians.every((g) => approvers.has(g))
-    : approvers.size >= 1;                                   // 'any' (default)
+  const enough = approvers.size >= followThreshold(guardians.length);
   if (enough) {
     stmts().setStatus.run('accepted', id);
Index: src/services/guardianship/outgoing.js
===================================================================
--- src/services/guardianship/outgoing.js	(revision d1075a16677d12c60b0d792c821a5baea451185d)
+++ src/services/guardianship/outgoing.js	(revision 3a0ca0f34e3f112899e4fdc916ec87010867dc3e)
@@ -15,4 +15,5 @@
  */
 import db from '../../config/database.js';
+import { followThreshold } from './follows.js';
 
 let _s = null;
@@ -80,7 +81,9 @@
   const approvers = new Set(rows.filter((r) => r.decision === 'approve').map((r) => r.guardian_uri));
   const guardians = (guardiansOfWard || []).filter(Boolean);
-  const enough = follow.quorum === 'all'
-    ? guardians.length > 0 && guardians.every((g) => approvers.has(g))
-    : approvers.size >= 1;                                   // 'any' (default)
+  // Dezelfde eenvoudige meerderheid als bij een inkomend volgverzoek
+  // (followThreshold): het is dezelfde vraag, alleen omgedraaid. Twee
+  // verschillende drempels voor "mag dit kind met deze persoon te maken hebben"
+  // zou een guardian nooit kunnen uitleggen.
+  const enough = approvers.size >= followThreshold(guardians.length);
   if (enough) {
     stmts().setStatus.run('approved', id);
