Changeset 86e6a45 in Klonkt


Ignore:
Timestamp:
08/08/2026 07:24:44 AM (5 weeks ago)
Author:
roboburr <roboburr@…>
Branches:
main
Children:
a9ede28
Parents:
0b0cd54
git-author:
Robin <roboburr@…> (08/08/2026 07:24:42 AM)
git-committer:
roboburr <roboburr@…> (08/08/2026 07:24:44 AM)
Message:

De apps krijgen de hulpstaat, en kunnen afhandelen (shaer-lgo)

Barts melding: afgehandelde hulpverzoeken blijven zichtbaar in de Shaer
GuardianshipView, en kan het afhandelen daar ook?

DIE TWEE ZIJN HETZELFDE PROBLEEM. De apps lazen hulpvragen uit de FEED -- losse
notes met een helpRequest-vlag -- en kregen de staat helemaal niet. Ze konden dus
niet weten of er al iemand op af was, en dan is een afgehandeld verzoek laten
staan nog het eerlijkste dat een app kan doen. Klonkt bewaarde de staat wel; hij
reisde alleen nergens heen.

Nu een queue help op de actor, met de staat PLAT erin: open, wie hem oppakte,
wie hem afsloot en wanneer. Een app hoeft hem niet af te leiden en kan hem dus
ook niet anders afleiden dan het paneel -- helpItemsFor is een plek, net als
wardGates. Twee berekeningen zouden twee guardians een ander beeld geven van
hetzelfde kind, en bij een reddingsboei is dat het gevaarlijkste dat er mis kan
gaan.

AFHANDELEN HOEFDE GEEN NIEUWE VORM. De markering IS al een gewone directe note
met shaer:helpPickup of shaer:helpHandled, precies zoals de zwaai. De outbox
herkent hem nu, dus de app stuurt letterlijk wat de PWA stuurt en het reist over
dezelfde bezorging naar de mede-guardians. Geen tweede weg.

Wel LOKAAL boeken, en daar staat een toets op: zonder dat zag de guardian die de
knop indrukt zijn eigen markering pas als hij bij zichzelf terugkwam, en die weg
bestaat niet.

Oppikken blijft OPEN. De faalstand hier is "iedereen denkt dat het geregeld is",
en die is gevaarlijker dan geen markering.

De AS2-toets ving dat help nog niet gedeclareerd stond op de actor -- precies
waarvoor die toets er is. Suite 718/718; zonder de lokale boeking valt er een om.

Files:
1 added
6 edited

Legend:

Unmodified
Added
Removed
  • src/routes/activitypub.js

    r0b0cd54 r86e6a45  
    314314// guardians' real states here and nowhere else.
    315315queueRoute('guardians', (id, slug) => Guardianship.guardiansCollection(id, slug));
     316// De hulpvragen MET hun staat (5.2.1, shaer-lgo). De apps lazen ze uit de feed
     317// en wisten dus niet of er al iemand op af was -- daarom bleef een afgehandeld
     318// verzoek daar staan (Barts melding, 8-8).
     319queueRoute('help', (id, slug) => Guardianship.helpCollection(id, slug));
    316320
    317321// ── Inbox read (owner only, AP C2S) ───────────────────────────────
  • src/services/ActivityPubService.js

    r0b0cd54 r86e6a45  
    33443344          }
    33453345          const gateReq = Guardianship.gatereq.parseRequest(object);
    3346           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 });
     3346          // Een hulpvraag oppikken of afsluiten vanuit de app (5.2.1, shaer-lgo).
     3347          // De markering IS al een gewone directe note met een shaer:-eigenschap,
     3348          // dus hier hoeft niets nieuws bij: de app stuurt precies wat de PWA
     3349          // stuurt, en het gaat over dezelfde bezorging naar de mede-guardians.
     3350          //
     3351          // We boeken hem ook LOKAAL. Zonder dat zou de guardian die de knop
     3352          // indrukt zijn eigen markering pas zien als hij bij zichzelf
     3353          // terugkomt -- en die weg bestaat niet.
     3354          const mark = Guardianship.help.parseMarker(object);
     3355          if (mark) {
     3356            const base2 = (process.env.PUBLIC_BASE_URL || '').replace(/\/+$/, '');
     3357            Guardianship.help.record(mark.noteUri, actorId(base2, site.slug), mark.kind, `@${site.slug}`);
     3358          }
     3359          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 });
    33473360          if (!r || !r.id) return { status: 502, error: 'direct_failed' };
    33483361          return { status: 201, id: r.id, url: `${base}/ap/notes/${r.id}` };
  • src/services/guardianship/index.js

    r0b0cd54 r86e6a45  
    1919export { wireDelivery, c2sVisibility, deliverDirectNote } from './delivery.js';
    2020export { wireHandshake, handleOutbox as handleGuardianshipOutbox, handleInbox as handleGuardianshipInbox, parseRelationship, parseUndoRelationship, endGuardianship } from './handshake.js';
    21 export { offersCollection, followsCollection, outgoingFollowsCollection, wardsCollection, guardiansCollection } from './queues.js';
     21export { offersCollection, followsCollection, outgoingFollowsCollection, wardsCollection, guardiansCollection, helpCollection } from './queues.js';
    2222export * as availability from './availability.js';
    2323export { wireAvailability } from './availability.js';
  • src/services/guardianship/queues.js

    r0b0cd54 r86e6a45  
    1616import * as gated from './gated.js';
    1717import * as gatereq from './gatereq.js';
     18import * as help from './help.js';
     19import db from '../../config/database.js';
    1820import * as handshake from './handshake.js';
    1921
     
    116118}
    117119
    118 export default { offersCollection, followsCollection, outgoingFollowsCollection, wardsCollection, guardiansCollection, wardGates, wardGuardianStatuses };
     120export default { offersCollection, followsCollection, outgoingFollowsCollection, wardsCollection, guardiansCollection, helpCollection, helpItemsFor, wardGates, wardGuardianStatuses };
    119121
    120122// ── Wat er voor een ward gated is (shaer-ahy.1) ─────────────────────────
     
    179181  });
    180182}
     183
     184
     185// ── Hulpvragen met hun staat (shaer-lgo, shaer-ahy.1) ───────────────────
     186//
     187// De PWA had dit al; de apps kregen alleen de losse notes uit de feed en wisten
     188// dus NIET of er al iemand op af was. Daarom bleef een afgehandeld verzoek daar
     189// gewoon staan -- Barts melding. De staat wordt hier een keer berekend, zoals bij
     190// wardGates: twee berekeningen zouden twee guardians een ander beeld geven van
     191// hetzelfde kind.
     192
     193/** De hulpvragen van deze guardian, met wie erop af is en of het dicht is. */
     194export function helpItemsFor(slug, limit = 50) {
     195  let rijen = [];
     196  try {
     197    rijen = db.prepare(
     198      `SELECT object_uri, actor_uri, actor_name, actor_handle, actor_icon, content, published, created_at
     199       FROM ap_mentions WHERE slug = ? AND help_request = 1 ORDER BY created_at DESC LIMIT ?`,
     200    ).all(slug, limit);
     201  } catch { return []; }
     202  const staat = help.statusFor(rijen.map((r) => r.object_uri));
     203  const mijn = new Set(relations.listWards(slug).map((w) => w.other_uri));
     204  return rijen.map((r) => ({
     205    ...r,
     206    // Bij twijfel OPEN. Een hulpvraag die er afgehandeld uitziet terwijl hij dat
     207    // niet is, is de gevaarlijke fout -- niet andersom.
     208    state: help.withWardship(
     209      staat.get(r.object_uri) || { open: true, pickedUpBy: [], handled: null, ageMs: null },
     210      mijn.has(r.actor_uri),
     211    ),
     212  }));
     213}
     214
     215/** Dezelfde vragen als collectie voor de apps (5.2.1). */
     216export function helpCollection(id, slug) {
     217  const items = helpItemsFor(slug).map((h) => ({
     218    id: h.object_uri,
     219    type: 'Note',
     220    attributedTo: h.actor_uri,
     221    'shaer:handle': h.actor_handle || undefined,
     222    content: h.content || '',
     223    published: h.published || h.created_at,
     224    'shaer:helpRequest': true,
     225    // De staat als platte velden: een app hoeft hem niet af te leiden, en kan
     226    // hem dus ook niet anders afleiden dan het paneel.
     227    'shaer:open': h.state.open,
     228    'shaer:handledBy': h.state.handled ? (h.state.handled.handle || h.state.handled.uri) : undefined,
     229    'shaer:handledAt': h.state.handled ? h.state.handled.at : undefined,
     230    'shaer:pickedUpBy': h.state.pickedUpBy.map((p) => p.handle || p.uri),
     231    'shaer:formerWard': h.state.formerWard || undefined,
     232  }));
     233  return collection(id, items);
     234}
  • src/services/guardianship/relations.js

    r0b0cd54 r86e6a45  
    8080      wards: `${id}/queues/wards`,
    8181      guardians: `${id}/queues/guardians`,
     82      help: `${id}/queues/help`,
    8283    },
    8384  };
  • test/activitypub-as2.test.js

    r0b0cd54 r86e6a45  
    4040  // `outgoingFollows` is §5.3 turned around: the ward's own follow requests,
    4141  // waiting for the guardians (shaer-p729).
    42   'shaer:queues', 'offers', 'follows', 'outgoingFollows', 'wards', 'guardians',
     42  // `help` is de vragenlijst met haar STAAT (5.2.1): de apps lazen hulpvragen uit
     43  // de feed en wisten niet of er al iemand op af was.
     44  'shaer:queues', 'offers', 'follows', 'outgoingFollows', 'wards', 'guardians', 'help',
    4345  // ActivityPub §4.1 `endpoints` vocabulary (same category as sharedInbox), used for C2S.
    4446  'oauthAuthorizationEndpoint', 'oauthTokenEndpoint', 'uploadMedia',
Note: See TracChangeset for help on using the changeset viewer.