Index: src/config/database.js
===================================================================
--- src/config/database.js	(revision bfa3ba3ce26883066a0df7053c97b2067ddaa1e6)
+++ src/config/database.js	(revision 7ccdced94ed9434ec28142de104082d14bd8cd78)
@@ -895,4 +895,10 @@
   // Rijen van voor deze kolom krijgen hun host hier, eenmalig.
   ensureColumn('ap_delivery', 'host', 'TEXT');
+  // Is deze rij al eens geprobeerd? Dan KAN hij aangekomen zijn, ook al gaf het
+  // een fout (een time-out nadat de ander hem verwerkte). Een Delete voor iets
+  // dat nooit de deur uitging, hoeft niet; voor iets dat misschien aankwam wel.
+  // Standaard 1: van rijen van voor deze kolom weten we het niet, en dan is
+  // "misschien aangekomen" de veilige kant. Nieuwe rijen zeggen het zelf.
+  ensureColumn('ap_delivery', 'tried', 'INTEGER DEFAULT 1');
   try {
     const zonder = db.prepare('SELECT id, inbox FROM ap_delivery WHERE host IS NULL').all();
Index: src/services/ActivityPubService.js
===================================================================
--- src/services/ActivityPubService.js	(revision bfa3ba3ce26883066a0df7053c97b2067ddaa1e6)
+++ src/services/ActivityPubService.js	(revision 7ccdced94ed9434ec28142de104082d14bd8cd78)
@@ -34,10 +34,10 @@
 import {
   safeFetch, getOrCreateKeys, deliver, fetchActor,
-  enqueueDelivery, deliverWithRetry, deliverNowOrQueue, processDeliveryQueue, startDeliveryWorker,
+  enqueueDelivery, deliverWithRetry, deliverNowOrQueue, cancelUndelivered, processDeliveryQueue, startDeliveryWorker,
   anySigningSlug, verifyRequest, signedGetHeaders, signedGetJson, apGetJson,
 } from './ap-transport.js';
 export {
   safeFetch, getOrCreateKeys, deliver, fetchActor,
-  enqueueDelivery, deliverWithRetry, deliverNowOrQueue, processDeliveryQueue, startDeliveryWorker,
+  enqueueDelivery, deliverWithRetry, deliverNowOrQueue, cancelUndelivered, processDeliveryQueue, startDeliveryWorker,
   verifyRequest, signedGetHeaders, signedGetJson,
 };
@@ -2850,9 +2850,9 @@
   inboxes.delete(`${me}/inbox`); inboxes.delete(`${base}/ap/inbox`);
   let delivered = 0;
+  // Langs de volgorde per inbox (shaer-xzab.3): een bewerking die vóór de nog
+  // wachtende Create aankomt, wordt genegeerd, en daarna brengt de Create de
+  // oude tekst.
   for (const inbox of [...inboxes].filter(Boolean)) {
-    let ok = false;
-    try { const st = await deliver(inbox, update, `${me}#main-key`, keys.private_pem); ok = st >= 200 && st < 300; } catch { ok = false; }
-    if (ok) delivered++;
-    else enqueueDelivery(site.slug, inbox, update); // durable: retry the edit later (was silently dropped)
+    if ((await deliverNowOrQueue(site.slug, inbox, update, `${me}#main-key`, keys.private_pem)) === 'sent') delivered++;
   }
   console.log('[AP] outreply edit', site.slug, 'delivered', delivered);
@@ -4152,10 +4152,8 @@
     return { delivered: false, inbox: null };
   }
-  try {
-    const st = await deliver(inbox, payload, `${me}#main-key`, keys.private_pem);
-    if (st >= 200 && st < 300) { console.log('[AP] guardianship', activity.type, 'delivered →', inbox, st); return { delivered: true, inbox }; }
-    console.warn('[AP] guardianship', activity.type, 'got', st, 'from', inbox, '→ queued for retry');
-  } catch (e) { console.warn('[AP] guardianship', activity.type, 'to', inbox, 'failed:', e.message, '→ queued for retry'); }
-  enqueueDelivery(site.slug, inbox, payload);
+  // Langs de volgorde per inbox (shaer-xzab.3), zoals elke bezorging.
+  const hoe = await deliverNowOrQueue(site.slug, inbox, payload, `${me}#main-key`, keys.private_pem);
+  if (hoe === 'sent') console.log('[AP] guardianship', activity.type, 'delivered →', inbox);
+  else console.warn('[AP] guardianship', activity.type, 'to', inbox, '→ queued for retry');
   return { delivered: true, inbox };   // queued: the retry worker gets it there
 }
Index: src/services/ap-transport.js
===================================================================
--- src/services/ap-transport.js	(revision bfa3ba3ce26883066a0df7053c97b2067ddaa1e6)
+++ src/services/ap-transport.js	(revision 7ccdced94ed9434ec28142de104082d14bd8cd78)
@@ -241,5 +241,5 @@
 function deliveryStmts() {
   if (!_insDeliv) {
-    _insDeliv = db.prepare('INSERT INTO ap_delivery (slug, inbox, host, body, attempts, next_at) VALUES (?,?,?,?,0,CURRENT_TIMESTAMP)');
+    _insDeliv = db.prepare('INSERT INTO ap_delivery (slug, inbox, host, body, attempts, next_at, tried) VALUES (?,?,?,?,0,CURRENT_TIMESTAMP,?)');
     // Alleen de OUDSTE openstaande rij per inbox: een nieuwere wacht op hem, ook
     // als hij zelf al aan de beurt zou zijn. Op id, want dat is de volgorde
@@ -260,7 +260,44 @@
   return { ins: _insDeliv, due: _dueDeliv, del: _delDeliv, bump: _bumpDeliv, host: _hostDeliv, off: _offDeliv, pendInbox: _pendInbox, hostWaits: _hostWaits };
 }
-export function enqueueDelivery(slug, inbox, activity) {
+// `tried`: ging er al een poging aan vooraf? Standaard ja, want zo riep iedereen
+// dit aan: na een mislukte deliver(). Alleen wie in de rij zet zonder te
+// proberen (deliverNowOrQueue bij een wachtende inbox of server) zegt nee.
+export function enqueueDelivery(slug, inbox, activity, { tried = true } = {}) {
   if (!slug || !inbox || !activity) return;
-  try { deliveryStmts().ins.run(slug, inbox, inboxHost(inbox), JSON.stringify(activity)); } catch { /* ignore */ }
+  try { deliveryStmts().ins.run(slug, inbox, inboxHost(inbox), JSON.stringify(activity), tried ? 1 : 0); } catch { /* ignore */ }
+}
+
+function objectIdOf(activity) {
+  const o = activity && activity.object;
+  return typeof o === 'string' ? o : (o && typeof o.id === 'string' ? o.id : null);
+}
+
+/**
+ * Een Delete van iets dat voor deze inbox nog in de rij staat (Robins vraag,
+ * 26-10: "shouldn't a Delete on an undelivered Create resolve internally?").
+ *
+ * Ja: haal de wachtende Create en Update van dat object uit de rij. Dan komt
+ * wat gewist is nooit meer aan bij een server die het nog niet had -- de
+ * volgorde per inbox alleen zorgde er wel voor dat de Delete NA de Create
+ * kwam, maar de Create kwam dan nog steeds.
+ *
+ * Of de Delete daarna nog hoeft, hangt af van of er iets aangekomen KAN zijn:
+ * een rij die al eens geprobeerd is, kan dat (een time-out nadat de ander hem
+ * verwerkte). Dan gaat de Delete toch; die is onschuldig als ze hem niet
+ * kenden, en zonder hem blijft de post daar misschien staan.
+ */
+export function cancelUndelivered(inbox, objectId) {
+  if (!inbox || !objectId) return { cancelled: 0, mayHaveArrived: false };
+  let cancelled = 0, mayHaveArrived = false;
+  try {
+    for (const r of db.prepare('SELECT id, body, attempts, tried FROM ap_delivery WHERE inbox = ?').all(inbox)) {
+      let a; try { a = JSON.parse(r.body); } catch { continue; }
+      if (!a || (a.type !== 'Create' && a.type !== 'Update') || objectIdOf(a) !== objectId) continue;
+      if (r.tried || r.attempts > 0) mayHaveArrived = true;
+      deliveryStmts().del.run(r.id);
+      cancelled++;
+    }
+  } catch { /* geen wachtrij te lezen: dan gewoon de Delete */ }
+  return { cancelled, mayHaveArrived };
 }
 // Record delivery health per follower so the followers list can flag dead accounts.
@@ -289,9 +326,16 @@
 // hoort (shaer-xzab.3). Drie plekken deden eerst zelf deliver() en pas bij een
 // mislukking enqueueDelivery(), en gingen zo langs de volgorde per inbox.
+//
+// 'cancelled': een Delete van iets dat deze inbox nooit kreeg; er gaat niets
+// heen (zie cancelUndelivered).
 export async function deliverNowOrQueue(slug, inbox, activity, keyId, privPem) {
   if (!inbox) return 'queued';
+  if (activity && activity.type === 'Delete') {
+    const r = cancelUndelivered(inbox, objectIdOf(activity));
+    if (r.cancelled && !r.mayHaveArrived) return 'cancelled';
+  }
   try {
     const st = deliveryStmts();
-    if (st.pendInbox.get(inbox) || st.hostWaits.get(inboxHost(inbox))) { enqueueDelivery(slug, inbox, activity); return 'queued'; }
+    if (st.pendInbox.get(inbox) || st.hostWaits.get(inboxHost(inbox))) { enqueueDelivery(slug, inbox, activity, { tried: false }); return 'queued'; }
   } catch { /* geen wachtrij te lezen: gewoon proberen */ }
   try { const st = await deliver(inbox, activity, keyId, privPem); if (st >= 200 && st < 300) { markFollowerDelivery(slug, inbox, true); return 'sent'; } } catch { /* queue below */ }
Index: test/direct-sends-queue.test.js
===================================================================
--- test/direct-sends-queue.test.js	(revision bfa3ba3ce26883066a0df7053c97b2067ddaa1e6)
+++ test/direct-sends-queue.test.js	(revision 7ccdced94ed9434ec28142de104082d14bd8cd78)
@@ -56,5 +56,8 @@
 const stil = (f) => async () => { const l = console.log, w = console.warn; console.log = () => {}; console.warn = () => {}; try { await f(); } finally { console.log = l; console.warn = w; } };
 
-test('een gewist antwoord komt niet aan vóór het antwoord zelf', stil(async () => {
+test('een gewist antwoord dat nog wachtte, komt nooit meer aan', stil(async () => {
+  // Robins vraag (26-10): een Delete van een Create die nog in de rij staat,
+  // hoort intern opgelost te worden. Eerst zorgde de volgorde per inbox er
+  // alleen voor dat de Delete NA de Create kwam -- maar de Create kwam dan nog.
   leeg();
   status = 503;                                            // de server van de volger hapert
@@ -66,10 +69,59 @@
   ontvangen.length = 0;
   await AP.deliverOutboxDelete(site, r.id);
-  assert.deepEqual(ontvangen, [], 'de Delete gaat niet voor');
-  assert.deepEqual(typen(), ['Create', 'Delete'], 'hij wacht achter de Create');
+  await AP.processDeliveryQueue();
+  assert.ok(!ontvangen.some((o) => o.type === 'Create'), 'het gewiste antwoord gaat niet meer de deur uit');
+  // Hij is wel één keer geprobeerd (de 503), en dan KAN hij aangekomen zijn:
+  // dan gaat de Delete toch, voor de zekerheid.
+  assert.deepEqual(ontvangen.map((o) => o.type), ['Delete']);
+  assert.deepEqual(rij(), []);
+}));
 
-  await AP.processDeliveryQueue();
-  await AP.processDeliveryQueue();
-  assert.deepEqual(ontvangen.map((o) => o.type), ['Create', 'Delete'], 'en zo komen ze aan');
+test('nooit geprobeerd en gewist: er gaat helemaal niets heen', stil(async () => {
+  leeg();
+  // De server zit in zijn backoff, dus het antwoord gaat meteen de rij in,
+  // zonder poging; daarna wordt het gewist.
+  AP.enqueueDelivery('me', `http://${host}/ander-pad`, { type: 'Create', id: 'https://klonkt.test/iets' });
+  db.prepare("UPDATE ap_delivery SET next_at = datetime('now', '+30 minutes')").run();
+  const r = await AP.deliverReply(site, { postId: '', postSlug: null, parent, text: 'nooit verstuurd' });
+  assert.equal(rij().filter((x) => x.inbox === inbox).length, 1, 'het antwoord wacht, ongeprobeerd');
+  await AP.deliverOutboxDelete(site, r.id);
+  assert.deepEqual(rij().filter((x) => x.inbox === inbox), [], 'Create weg, en geen Delete erachteraan');
+  assert.deepEqual(ontvangen, []);
+  assert.equal(rij().length, 1, 'wat er voor een andere inbox wachtte, blijft');
+}));
+
+test('alleen de Create en Update van DAT object, en alleen voor die inbox', () => {
+  leeg();
+  const obj = 'https://klonkt.test/ap/notes/x';
+  AP.enqueueDelivery('me', inbox, { type: 'Create', id: `${obj}#create`, object: { id: obj } }, { tried: false });
+  AP.enqueueDelivery('me', inbox, { type: 'Update', id: `${obj}#update`, object: { id: obj } }, { tried: false });
+  AP.enqueueDelivery('me', inbox, { type: 'Create', id: 'y#create', object: { id: 'https://klonkt.test/ap/notes/y' } }, { tried: false });
+  AP.enqueueDelivery('me', `http://${host}/elders`, { type: 'Create', id: `${obj}#create`, object: { id: obj } }, { tried: false });
+  // Bewust ALLEEN wat de inhoud draagt. Een Announce of Add van een gewist
+  // object is onschuldig: de ontvanger haalt het op en krijgt een 404.
+  AP.enqueueDelivery('me', inbox, { type: 'Announce', id: `${obj}#boost`, object: obj }, { tried: false });
+  const r = AP.cancelUndelivered(inbox, obj);
+  assert.deepEqual(r, { cancelled: 2, mayHaveArrived: false });
+  assert.deepEqual(rij().map((x) => [x.inbox.endsWith('/inbox') ? 'deze' : 'andere', JSON.parse(x.body).type, String(JSON.parse(x.body).object.id || JSON.parse(x.body).object).slice(-1)]),
+    [['deze', 'Create', 'y'], ['andere', 'Create', 'x'], ['deze', 'Announce', 'x']]);
+});
+
+test('een bewerking gaat niet voorbij het antwoord dat nog wacht', stil(async () => {
+  leeg();
+  status = 503;
+  const r = await AP.deliverReply(site, { postId: '', postSlug: null, parent, text: 'origineel' });
+  status = 202; ontvangen.length = 0;
+  await AP.deliverOutboxUpdate(site, r.id, 'bewerkt');
+  assert.deepEqual(ontvangen, [], 'de Update gaat niet voor: dan negeert de ander hem, en brengt de Create daarna de oude tekst');
+  assert.deepEqual(typen(), ['Create', 'Update']);
+}));
+
+test('een guardian-activiteit gaat niet voorbij wat er voor die inbox wacht', stil(async () => {
+  leeg();
+  const omaInbox = `http://${host}/users/oma/inbox`;
+  AP.enqueueDelivery('me', omaInbox, { type: 'Create', id: 'https://klonkt.test/eerder' });
+  await AP.deliverToActor(site, `http://${host}/users/oma`, { type: 'Offer', id: 'https://klonkt.test/offer/1' });
+  assert.deepEqual(ontvangen, []);
+  assert.equal(rij().filter((x) => x.inbox === omaInbox).length, 2);
 }));
 
