Changeset 709dc6f in Klonkt


Ignore:
Timestamp:
08/09/2026 04:59:31 PM (4 weeks ago)
Author:
Bart <bart@…>
Branches:
main
Children:
89d3c06
Parents:
e62eee4
Message:

Twee richtingen, twee poorten: shaer:following naast shaer:follows

In de catalogus stond één rij voor twee mechanismen. Het paneel telde
listReviewsByDirection(slug, 'incoming') en zette dat getal onder
shaer:follows, dus een guardian las "3 wachtend" en wist niet of er drie
vreemden bij zijn kind wilden of dat zijn kind drie keer had gevraagd of het
iemand mocht volgen. Dat zijn niet dezelfde zorg, en sinds shaer-p729 bestaan
ze allebei echt.

Nu twee poorten, en het verschil ertussen is opzet. §5.3 EIST dat een Follow
naar een ward langs de guardians gaat: shaer:follows blijft dus fixed. Over de
andere richting zegt de FEP niets — wat je verder gated is expliciet aan de
implementatie gelaten — dus shaer:following is onze keuze, en dan hoort hij ook
echt losgelaten te kunnen worden. Verstelbaar, met gate_following als kolom en
dezelfde automatiek als de rest: onbeslist is dicht voor een ward en open voor
ieder ander. Een kind dat erin groeit hoeft niet eeuwig te blijven vragen.

Geen eigen ownFollowsAllowed(): wardGateAllowed() is er al en zegt er zelf bij
dat het één implementatie hoort te zijn. Een derde kopie zou precies de tweede
plek zijn die er anders over kan gaan denken.

De labels zijn nog aan de clients: de server geeft de feature-naam door, de
Guardian PWA en Shaer moeten er nog woorden bij kiezen.

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

Files:
5 edited

Legend:

Unmodified
Added
Removed
  • src/config/database.js

    re62eee4 r709dc6f  
    177177  ensureColumn('sites', 'gate_custom_emoji', 'INTEGER');  // FEP-9098 emoji-plaatjes (shaer-ytw)
    178178  ensureColumn('sites', 'gate_account_move', 'INTEGER');  // FEP-7628 Move (shaer-tge)
     179  // Wie de ward ZELF mag volgen (shaer-p729): de tegenhanger van shaer:follows,
     180  // dat over de andere richting gaat. §5.3 schrijft alleen het doorsturen voor
     181  // van een Follow NAAR een ward; wat je verder gated is een keuze van de
     182  // implementatie, en dit is die keuze. Verstelbaar, anders dan de inkomende
     183  // kant: een kind dat ouder wordt hoort niet eeuwig te blijven vragen.
     184  ensureColumn('sites', 'gate_following', 'INTEGER');     // zelf iemand volgen (shaer-p729)
    179185  // The heavier sibling (FEP-633c 5.6): may a player from outside this app run
    180186  // INSIDE it? A preview is a picture; playback hands the screen to a third
  • src/services/ActivityPubService.js

    re62eee4 r709dc6f  
    54765476  const guardians = Guardianship.listGuardians(slug).map((g) => g.other_uri);
    54775477  if (!guardians.length) return null;                                   // not a ward: nothing to gate
     5478  // shaer:following (shaer-p729) — its own gate, apart from shaer:follows,
     5479  // which governs the OTHER direction. §5.3 fixes the inbound one on: a Follow
     5480  // aimed at a ward MUST pass the guardians. About this direction the FEP says
     5481  // nothing, so it is ours to set and ours to let go of, and the guardians can
     5482  // relax it for a child who has grown into it. Undecided means gated for a
     5483  // ward, the same automatiek as the rest of the family.
     5484  const gateRow = db.prepare('SELECT gate_following FROM sites WHERE slug = ?').get(slug);
     5485  if (Guardianship.wardGateAllowed(gateRow && gateRow.gate_following, true)) return null;
    54785486  if (guardians.includes(targetUri)) return null;                       // your own guardian
    54795487  if (Guardianship.outgoing.isMutual(slug, targetUri)) return null;     // already vetted by name
  • src/services/guardianship/gated.js

    re62eee4 r709dc6f  
    6868  'shaer:customEmoji': 'gate_custom_emoji',
    6969  'shaer:accountMove': 'gate_account_move',
     70  'shaer:following': 'gate_following',
    7071};
    7172/**
     
    112113  { feature: 'shaer:customEmoji', kind: 'setting', reversible: true, bead: 'shaer-ytw' },
    113114  { feature: 'shaer:accountMove', kind: 'setting', reversible: true, bead: 'shaer-tge' },
    114   // Altijd aan voor een ward (5.3): niet te verzetten, wel te tonen. Een paneel
    115   // dat alleen verstelbare dingen laat zien verzwijgt de helft van wat er geldt.
     115  // Wie de ward mag VOLGEN, en wie de ward mag volgen: twee poorten, want twee
     116  // vragen. Ze stonden hier als één rij, en dan telt het paneel de ene richting
     117  // en zwijgt over de andere -- een guardian ziet "follows: 3 wachtend" en weet
     118  // niet of er drie vreemden bij zijn kind willen of dat zijn kind drie keer
     119  // heeft gevraagd of het iemand mag volgen. Dat zijn niet dezelfde zorg.
     120  //
     121  // Inkomend is vast: §5.3 EIST dat een Follow naar een ward langs de guardians
     122  // gaat, dus die staat aan en blijft aanstaan. Tonen mag, verzetten niet.
    116123  { feature: 'shaer:follows', kind: 'perRequest', reversible: true, fixed: true },
     124  // Uitgaand is verstelbaar, en dat verschil is opzet. De FEP zegt over deze
     125  // richting niets: §5.3 gaat alleen over een Follow die op een ward AF komt.
     126  // Wat je verder gated is expliciet aan de implementatie gelaten, dus dit is
     127  // onze keuze en niet die van de spec -- en dan hoort hij ook echt te kunnen
     128  // worden losgelaten, want een kind dat ouder wordt hoort niet eeuwig te
     129  // blijven vragen wie het mag volgen (shaer-p729, shaer-yeo5).
     130  { feature: 'shaer:following', kind: 'perRequest', reversible: true, bead: 'shaer-p729' },
    117131
    118132  // GEPLAND, en dat is bij deze twee geen achterstand maar een besluit.
  • src/services/guardianship/queues.js

    re62eee4 r709dc6f  
    158158export function wardGates(mySlug, wardUri) {
    159159  const statuses = wardGuardianStatuses(wardUri);
    160   const wachtend = follows.listReviewsByDirection(mySlug, 'incoming')
     160  // Per richting geteld, want het zijn twee zorgen. "follows: 3 wachtend" liet
     161  // een guardian niet zien of er drie vreemden bij zijn kind willen of dat zijn
     162  // kind drie keer heeft gevraagd of het iemand mag volgen (shaer-p729).
     163  const wachtendIn = follows.listReviewsByDirection(mySlug, 'incoming')
     164    .filter((r) => r.ward_uri === wardUri).length;
     165  const wachtendUit = follows.listReviewsByDirection(mySlug, 'outgoing')
    161166    .filter((r) => r.ward_uri === wardUri).length;
    162167  return gated.gateRows({
     
    172177      feature: p.feature, value: !!p.value, status: gated.sentStatus(p, Date.now()),
    173178    })),
    174     waiting: { 'shaer:follows': wachtend || undefined },
     179    waiting: {
     180      'shaer:follows': wachtendIn || undefined,
     181      'shaer:following': wachtendUit || undefined,
     182    },
    175183    // De vraag van het kind zelf staat APART van wat er in een wachtrij staat
    176184    // (shaer-8ru). Allebei "n waiting" noemen maakt van twee verschillende
  • test/outgoing-follow-gate.test.js

    re62eee4 r709dc6f  
    152152  assert.notEqual(uit && uit.held, true);
    153153});
     154
     155// ── shaer:following als eigen poort (shaer-p729) ──────────────────────────
     156// De uitgaande kant stond als één rij met de inkomende in het paneel, en dan
     157// telt een guardian de ene richting en hoort niets over de andere. Nu twee
     158// poorten. Het VERSCHIL tussen die twee is opzet: §5.3 eist dat een Follow
     159// NAAR een ward langs de guardians gaat, dus die staat vast aan. Over deze
     160// richting zegt de FEP niets, dus die is van ons -- en dan hoort hij ook echt
     161// losgelaten te kunnen worden.
     162
     163test('onbeslist betekent dicht voor een ward, net als bij de andere poorten', async () => {
     164  const wim = site('wim');
     165  guards('wim', local('mum'));
     166  const held = await AP.gateOutgoingFollow(wim, 'https://elders.example/users/nieuw');
     167  assert.ok(held, 'niemand heeft er iets over besloten, dus vragen we');
     168});
     169
     170test('de guardians kunnen de poort openzetten, en dan vraagt het kind niets meer', async () => {
     171  const zoe = site('zoe');
     172  guards('zoe', local('mum'));
     173  db.prepare('UPDATE sites SET gate_following = 1 WHERE slug = ?').run('zoe');
     174  const zoeSite = db.prepare('SELECT * FROM sites WHERE slug = ?').get('zoe');
     175  assert.equal(await AP.gateOutgoingFollow(zoeSite, 'https://elders.example/users/nieuw'), null,
     176    'een kind dat erin gegroeid is hoeft niet eeuwig te blijven vragen');
     177});
     178
     179test('en weer dicht is ook een besluit', async () => {
     180  const zoeSite = db.prepare('SELECT * FROM sites WHERE slug = ?').get('zoe');
     181  db.prepare('UPDATE sites SET gate_following = 0 WHERE slug = ?').run('zoe');
     182  const dicht = db.prepare('SELECT * FROM sites WHERE slug = ?').get('zoe');
     183  assert.ok(await AP.gateOutgoingFollow(dicht, 'https://elders.example/users/weer'),
     184    'terugdraaien kan: de poort is reversible');
     185  assert.equal(zoeSite.gate_following, 1, '(en de oude rij was echt open)');
     186});
     187
     188test('de twee richtingen tellen apart in het paneel', async () => {
     189  const queues = await import('../src/services/guardianship/queues.js');
     190  const rows = queues.wardGates('mum', local('kid'));
     191  const namen = rows.map((r) => r.feature);
     192  assert.ok(namen.includes('shaer:follows'), 'wie het kind wil volgen');
     193  assert.ok(namen.includes('shaer:following'), 'en wie het kind wil volgen -- andersom');
     194  const uit = rows.find((r) => r.feature === 'shaer:following');
     195  assert.equal(uit.adjustable, true, 'deze mag verzet worden');
     196  const inn = rows.find((r) => r.feature === 'shaer:follows');
     197  assert.equal(inn.adjustable, false, 'en deze niet: §5.3 laat er geen ruimte voor');
     198});
Note: See TracChangeset for help on using the changeset viewer.