Changeset 7e53594 in Klonkt


Ignore:
Timestamp:
08/10/2026 03:53:26 AM (4 weeks ago)
Author:
Bart <bart@…>
Branches:
main
Children:
0db3c72
Parents:
f06ed56
Message:

shaer:log: het logboek staat naast de wachtrijen, niet erin

De apps konden er niet bij. Het logboek zat achter /guardian/api/events, een
PWA-route, dus een ward kon niet lezen waarom haar eigen aanbod geweigerd was --
en dat is precies de helft die §4.2 eist.

Eigen sleutel op de actor, GET /ap/users/:slug/log, dezelfde eigenaar-only
bearer als de wachtrijen. Bewust niet onder shaer:queues: alles daar wacht op
een antwoord en dit is wat er al besloten is. Geschiedenis onderbrengen bij een
woord dat "wachtend" betekent maakt van twee dingen één, en dat is de fout die
deze module vandaag drie keer heeft opgeruimd.

type is 'shaer:Event' en geen AS2-werkwoord. De meeste soorten zijn er geen: een
lapse-stem of een opgepakte hulpvraag is geen Accept, en het zo noemen zou
netter lezen dan het is.

Clients vinden hem via het actor-document, niet via een pad dat ze zelf
verzinnen (shaer-qa0). De AS2-test viel netjes om tot shaer:log was aangemeld --
tweede keer vandaag dat die het vangt.

De daemon volgt (shaer-6d9): zolang die geen shaer:log serveert, liegt een
client die tegen beide praat over een van de twee.

Co-Authored-By: Claude Opus 5 <claude@…>

Files:
6 edited

Legend:

Unmodified
Added
Removed
  • src/routes/activitypub.js

    rf06ed56 r7e53594  
    344344// guardians' real states here and nowhere else.
    345345queueRoute('guardians', (id, slug) => Guardianship.guardiansCollection(id, slug));
     346
     347// ── Het logboek (FEP-633c §4.2, shaer:log) ────────────────────────────
     348// NAAST de wachtrijen en niet erin: alles onder shaer:queues wacht op een
     349// antwoord, dit is wat er al besloten is. Eigen pad, dezelfde eigenaar-only
     350// bearer. Het bestaat omdat een weigering anders alleen te merken viel doordat
     351// er iets uit een lijst verdween, en "het is weg" is geen reden.
     352router.get('/ap/users/:slug/log', (req, res) => {
     353  const auth = OAuth.verifyBearer(req.headers.authorization);
     354  if (!auth || auth.site.slug !== req.params.slug) return res.status(403).end();
     355  const me = `${baseUrl(req)}/ap/users/${auth.site.slug}`;
     356  AP.sendAP(res, {
     357    '@context': AP.AP_CONTEXT,
     358    ...Guardianship.logCollection(`${me}/log`, auth.site.slug, (s) => AP.listGuardianEvents(s, 50)),
     359  }, 'private, no-store');
     360});
    346361// De hulpvragen MET hun staat (5.2.1, shaer-lgo). De apps lazen ze uit de feed
    347362// en wisten dus niet of er al iemand op af was -- daarom bleef een afgehandeld
  • src/services/guardianship/index.js

    rf06ed56 r7e53594  
    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, helpCollection } from './queues.js';
     21export { offersCollection, followsCollection, outgoingFollowsCollection, logCollection, wardsCollection, guardiansCollection, helpCollection } from './queues.js';
    2222export * as availability from './availability.js';
    2323export { wireAvailability } from './availability.js';
  • src/services/guardianship/queues.js

    rf06ed56 r7e53594  
    118118}
    119119
    120 export default { offersCollection, followsCollection, outgoingFollowsCollection, wardsCollection, guardiansCollection, helpCollection, helpItemsFor, wardGates, wardGuardianStatuses };
     120/**
     121 * Het logboek (§4.2): wat er is besloten, en waarom.
     122 *
     123 * Geen wachtrij, en daarom een eigen sleutel op de actor. Elk item draagt zijn
     124 * soort en, als die er was, de REDEN -- want zonder die reden merkte een ward
     125 * een weigering alleen doordat er iets uit een lijst verdween.
     126 *
     127 * `type` is geen AS2-werkwoord: de meeste soorten zijn er geen. Een lapse-stem
     128 * of een opgepakte hulpvraag is geen Accept, en het zo noemen zou netter lezen
     129 * dan het is.
     130 *
     131 * De lezer levert `listEvents` aan; deze module kent ActivityPubService niet en
     132 * houdt dat zo (zie de kop van delivery.js).
     133 */
     134export function logCollection(id, slug, listEvents) {
     135  const items = (typeof listEvents === 'function' ? listEvents(slug) : []).map((e) => {
     136    const { id: n, kind, created, ...rest } = e;
     137    const item = { id: `${id}/${n}`, type: 'shaer:Event', 'shaer:kind': kind, published: created };
     138    for (const [k, v] of Object.entries(rest)) {
     139      if (v === undefined || v === null) continue;
     140      item[k.startsWith('shaer:') ? k : `shaer:${k}`] = v;
     141    }
     142    return item;
     143  });
     144  return collection(id, items);
     145}
     146
     147export default { offersCollection, followsCollection, outgoingFollowsCollection, wardsCollection, guardiansCollection, helpCollection, helpItemsFor, wardGates, wardGuardianStatuses, logCollection };
    121148
    122149// ── Wat er voor een ward gated is (shaer-ahy.1) ─────────────────────────
  • src/services/guardianship/relations.js

    rf06ed56 r7e53594  
    8282      help: `${id}/queues/help`,
    8383    },
     84    // NAAST de wachtrijen, niet erin. Alles onder shaer:queues wacht op een
     85    // antwoord; dit is wat er al besloten is, met de reden erbij (§4.2).
     86    // Geschiedenis onderbrengen bij een woord dat "wachtend" betekent maakt van
     87    // twee dingen één, en dat is precies de fout die de rest van deze module
     88    // net heeft opgeruimd.
     89    'shaer:log': `${id}/log`,
    8490  };
    8591  const guardians = listGuardians(slug).map((r) => r.other_uri);
  • test/activitypub-as2.test.js

    rf06ed56 r7e53594  
    4545  // `help` is de vragenlijst met haar STAAT (5.2.1): de apps lazen hulpvragen uit
    4646  // de feed en wisten niet of er al iemand op af was.
    47   'shaer:queues', 'offers', 'follows', 'outgoingFollows', 'wards', 'guardians', 'help',
     47  'shaer:queues', 'offers', 'follows', 'outgoingFollows', 'wards', 'guardians',
     48  // §4.2: het logboek staat NAAST de wachtrijen, want het wacht nergens op.
     49  'shaer:log', 'help',
    4850  // Elke OPEN hulpvraag zit in de collectie; daarom mag een app uit afwezigheid
    4951  // concluderen dat iets niet open is (shaer-6wt).
  • test/guardian-event-log.test.js

    rf06ed56 r7e53594  
    7676  assert.doesNotThrow(() => AP.onGuardianshipEvent(null, { kind: 'setting' }));
    7777});
     78
     79// ── De collectie die de apps lezen (shaer:log) ────────────────────────────
     80// Het logboek staat NAAST shaer:queues. Alles daarin wacht op een antwoord; dit
     81// is wat er al besloten is. Een aparte sleutel dus, en een aparte vorm.
     82
     83test('de actor wijst het logboek aan, los van de wachtrijen', async () => {
     84  const G = await import('../src/services/guardianship/index.js');
     85  const props = G.guardianshipActorProps('https://test.example/ap/users/kid', 'kid');
     86  assert.equal(props['shaer:log'], 'https://test.example/ap/users/kid/log');
     87  assert.equal(props['shaer:queues'].log, undefined,
     88    'geen geschiedenis onder een woord dat "wachtend" betekent');
     89});
     90
     91test('elk item draagt zijn soort en zijn reden mee', async () => {
     92  const G = await import('../src/services/guardianship/index.js');
     93  const c = G.logCollection('https://test.example/ap/users/kid/log', 'kid',
     94    (s) => AP.listGuardianEvents(s, 50));
     95  assert.equal(c.type, 'OrderedCollection');
     96  const weigering = c.orderedItems.find((i) => i['shaer:kind'] === 'offer_rejected');
     97  assert.ok(weigering, 'de weigering staat erin');
     98  assert.equal(weigering['shaer:reason'], 'not_a_teapot',
     99    'MET de reden: daarvoor bestaat dit ding');
     100  assert.equal(weigering['shaer:candidate'], 'https://elders.example/users/tess');
     101  assert.ok(weigering.published, 'en wanneer');
     102  // Geen AS2-werkwoord: een lapse-stem of een opgepakte hulpvraag is geen
     103  // Accept, en het zo noemen zou netter lezen dan het is.
     104  assert.equal(weigering.type, 'shaer:Event');
     105});
     106
     107test('een leeg logboek is een lege collectie, geen fout', async () => {
     108  const G = await import('../src/services/guardianship/index.js');
     109  const c = G.logCollection('https://test.example/ap/users/niemand/log', 'niemand',
     110    (s) => AP.listGuardianEvents(s, 50));
     111  assert.equal(c.totalItems, 0);
     112  assert.deepEqual(c.orderedItems, []);
     113});
Note: See TracChangeset for help on using the changeset viewer.