Index: src/routes/activitypub.js
===================================================================
--- src/routes/activitypub.js	(revision 0b0cd54af139c41ca90ad8f48c8d5532aa692616)
+++ src/routes/activitypub.js	(revision 86e6a45456a85581974a889cf6f6eccef3c18081)
@@ -314,4 +314,8 @@
 // guardians' real states here and nowhere else.
 queueRoute('guardians', (id, slug) => Guardianship.guardiansCollection(id, slug));
+// De hulpvragen MET hun staat (5.2.1, shaer-lgo). De apps lazen ze uit de feed
+// en wisten dus niet of er al iemand op af was -- daarom bleef een afgehandeld
+// verzoek daar staan (Barts melding, 8-8).
+queueRoute('help', (id, slug) => Guardianship.helpCollection(id, slug));
 
 // ── Inbox read (owner only, AP C2S) ───────────────────────────────
Index: src/services/ActivityPubService.js
===================================================================
--- src/services/ActivityPubService.js	(revision 0b0cd54af139c41ca90ad8f48c8d5532aa692616)
+++ src/services/ActivityPubService.js	(revision 86e6a45456a85581974a889cf6f6eccef3c18081)
@@ -3344,5 +3344,18 @@
           }
           const gateReq = Guardianship.gatereq.parseRequest(object);
-          const r = await deliverDirectNote(site, { recipients, text: plain, language: object.language || null, inReplyTo: typeof object.inReplyTo === 'string' ? object.inReplyTo : null, attachments: atts, helpRequest: help, awayUntil, gateRequest: gateReq && gateReq.feature });
+          // Een hulpvraag oppikken of afsluiten vanuit de app (5.2.1, shaer-lgo).
+          // De markering IS al een gewone directe note met een shaer:-eigenschap,
+          // dus hier hoeft niets nieuws bij: de app stuurt precies wat de PWA
+          // stuurt, en het gaat over dezelfde bezorging naar de mede-guardians.
+          //
+          // We boeken hem ook LOKAAL. Zonder dat zou de guardian die de knop
+          // indrukt zijn eigen markering pas zien als hij bij zichzelf
+          // terugkomt -- en die weg bestaat niet.
+          const mark = Guardianship.help.parseMarker(object);
+          if (mark) {
+            const base2 = (process.env.PUBLIC_BASE_URL || '').replace(/\/+$/, '');
+            Guardianship.help.record(mark.noteUri, actorId(base2, site.slug), mark.kind, `@${site.slug}`);
+          }
+          const r = await deliverDirectNote(site, { recipients, text: plain, language: object.language || null, inReplyTo: typeof object.inReplyTo === 'string' ? object.inReplyTo : null, attachments: atts, helpRequest: help, awayUntil, gateRequest: gateReq && gateReq.feature, helpMark: mark });
           if (!r || !r.id) return { status: 502, error: 'direct_failed' };
           return { status: 201, id: r.id, url: `${base}/ap/notes/${r.id}` };
Index: src/services/guardianship/index.js
===================================================================
--- src/services/guardianship/index.js	(revision 0b0cd54af139c41ca90ad8f48c8d5532aa692616)
+++ src/services/guardianship/index.js	(revision 86e6a45456a85581974a889cf6f6eccef3c18081)
@@ -19,5 +19,5 @@
 export { wireDelivery, c2sVisibility, deliverDirectNote } from './delivery.js';
 export { wireHandshake, handleOutbox as handleGuardianshipOutbox, handleInbox as handleGuardianshipInbox, parseRelationship, parseUndoRelationship, endGuardianship } from './handshake.js';
-export { offersCollection, followsCollection, outgoingFollowsCollection, wardsCollection, guardiansCollection } from './queues.js';
+export { offersCollection, followsCollection, outgoingFollowsCollection, wardsCollection, guardiansCollection, helpCollection } from './queues.js';
 export * as availability from './availability.js';
 export { wireAvailability } from './availability.js';
Index: src/services/guardianship/queues.js
===================================================================
--- src/services/guardianship/queues.js	(revision 0b0cd54af139c41ca90ad8f48c8d5532aa692616)
+++ src/services/guardianship/queues.js	(revision 86e6a45456a85581974a889cf6f6eccef3c18081)
@@ -16,4 +16,6 @@
 import * as gated from './gated.js';
 import * as gatereq from './gatereq.js';
+import * as help from './help.js';
+import db from '../../config/database.js';
 import * as handshake from './handshake.js';
 
@@ -116,5 +118,5 @@
 }
 
-export default { offersCollection, followsCollection, outgoingFollowsCollection, wardsCollection, guardiansCollection, wardGates, wardGuardianStatuses };
+export default { offersCollection, followsCollection, outgoingFollowsCollection, wardsCollection, guardiansCollection, helpCollection, helpItemsFor, wardGates, wardGuardianStatuses };
 
 // ── Wat er voor een ward gated is (shaer-ahy.1) ─────────────────────────
@@ -179,2 +181,54 @@
   });
 }
+
+
+// ── Hulpvragen met hun staat (shaer-lgo, shaer-ahy.1) ───────────────────
+//
+// De PWA had dit al; de apps kregen alleen de losse notes uit de feed en wisten
+// dus NIET of er al iemand op af was. Daarom bleef een afgehandeld verzoek daar
+// gewoon staan -- Barts melding. De staat wordt hier een keer berekend, zoals bij
+// wardGates: twee berekeningen zouden twee guardians een ander beeld geven van
+// hetzelfde kind.
+
+/** De hulpvragen van deze guardian, met wie erop af is en of het dicht is. */
+export function helpItemsFor(slug, limit = 50) {
+  let rijen = [];
+  try {
+    rijen = db.prepare(
+      `SELECT object_uri, actor_uri, actor_name, actor_handle, actor_icon, content, published, created_at
+       FROM ap_mentions WHERE slug = ? AND help_request = 1 ORDER BY created_at DESC LIMIT ?`,
+    ).all(slug, limit);
+  } catch { return []; }
+  const staat = help.statusFor(rijen.map((r) => r.object_uri));
+  const mijn = new Set(relations.listWards(slug).map((w) => w.other_uri));
+  return rijen.map((r) => ({
+    ...r,
+    // Bij twijfel OPEN. Een hulpvraag die er afgehandeld uitziet terwijl hij dat
+    // niet is, is de gevaarlijke fout -- niet andersom.
+    state: help.withWardship(
+      staat.get(r.object_uri) || { open: true, pickedUpBy: [], handled: null, ageMs: null },
+      mijn.has(r.actor_uri),
+    ),
+  }));
+}
+
+/** Dezelfde vragen als collectie voor de apps (5.2.1). */
+export function helpCollection(id, slug) {
+  const items = helpItemsFor(slug).map((h) => ({
+    id: h.object_uri,
+    type: 'Note',
+    attributedTo: h.actor_uri,
+    'shaer:handle': h.actor_handle || undefined,
+    content: h.content || '',
+    published: h.published || h.created_at,
+    'shaer:helpRequest': true,
+    // De staat als platte velden: een app hoeft hem niet af te leiden, en kan
+    // hem dus ook niet anders afleiden dan het paneel.
+    'shaer:open': h.state.open,
+    'shaer:handledBy': h.state.handled ? (h.state.handled.handle || h.state.handled.uri) : undefined,
+    'shaer:handledAt': h.state.handled ? h.state.handled.at : undefined,
+    'shaer:pickedUpBy': h.state.pickedUpBy.map((p) => p.handle || p.uri),
+    'shaer:formerWard': h.state.formerWard || undefined,
+  }));
+  return collection(id, items);
+}
Index: src/services/guardianship/relations.js
===================================================================
--- src/services/guardianship/relations.js	(revision 0b0cd54af139c41ca90ad8f48c8d5532aa692616)
+++ src/services/guardianship/relations.js	(revision 86e6a45456a85581974a889cf6f6eccef3c18081)
@@ -80,4 +80,5 @@
       wards: `${id}/queues/wards`,
       guardians: `${id}/queues/guardians`,
+      help: `${id}/queues/help`,
     },
   };
