source: Klonkt/src/services/guardianship/queues.js@ fa33214

main
Last change on this file since fa33214 was fa33214, checked in by Bart <bart@…>, 5 weeks ago

FEP-633c §5.3 andersom: een ward vraagt eerst of het iemand mag volgen

Uitgaande follows gingen ongehinderd de deur uit; de guardians kregen achteraf
een bericht (1a2f206). Dat is informeren, niet gaten — de deur staat al open als
het bericht aankomt. Bead shaer-p729, ontwerp in
docs/ward-outbound-follows-design.md.

De regel: per geval goedkeuring, met twee uitzonderingen die geen gunst zijn
maar dezelfde beslissing die al genomen is. Je eigen guardian volgen is geen
vraag. En iemand die de ward al volgt DOOR DE POORT heen is door een guardian
bij naam goedgekeurd; die vraag nog eens stellen leert mensen alleen om de vraag
niet meer te lezen.

Daarvoor moet je weten wie er door de poort kwam, dus ap_followers krijgt
gate_approved, gezet bij acceptGatedFollow. Iedereen die al volgde toen die
kolom erbij kwam wordt eenmalig gegrandfatherd (Barts besluit): exact vanaf nu,
in plaats van met terugwerkende kracht wantrouwig tegen wat er al was.

Eigen tabel, want ap_pending_follows is gesleuteld met de ward als DOEL. Eigen
wachtrij (outgoingFollows), want een guardian moet "iemand wil je ward volgen"
kunnen onderscheiden van "je ward wil iemand volgen" — de AS2-test ving netjes
dat de nieuwe term aangemeld moest worden. En een tegengehouden follow reist als
derde uitkomst naar de app (state: awaiting_guardian), zodat Shaer "wacht op
toestemming" kan tonen in plaats van een tegel die er al volgend uitziet.

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

  • Property mode set to 100644
File size: 2.9 KB
Line 
1/**
2 * Guardianship (FEP-633c) — the owner-only dashboard queues.
3 *
4 * Three OrderedCollections on the actor (shaer:queues), same contract as the
5 * Shaer test daemon so the iOS/Android dashboards read them as-is:
6 * - offers: pending handshake offers where I am a party (§3), with the full
7 * accept tally so the client shows the right action
8 * - follows: pending gated follows for my wards (§5.3) — Fase 2, empty for now
9 * - wards: my committed wards
10 */
11import * as offers from './offers.js';
12import * as relations from './relations.js';
13import * as availability from './availability.js';
14import * as outgoing from './outgoing.js';
15import * as handshake from './handshake.js';
16
17const collection = (id, items) => ({
18 id, type: 'OrderedCollection', totalItems: items.length, orderedItems: items,
19});
20
21/** Pending offers where the local site is a party, each with its accept
22 * tally. The same collection carries the running lapses (§3.6.3) this
23 * account is a party to, exactly as the daemon serves them, so the Shaer
24 * clients render both without a second fetch. */
25export function offersCollection(id, slug, me) {
26 // §4.2: a handshake whose candidate could not be dereferenced is deferred,
27 // not decided, and the last Accept may already have landed — so nothing else
28 // would ever retry it. This poll is the schedule. Not awaited: the read
29 // answers with what is true now, and a retry that succeeds surfaces in the
30 // next one. `listForParty` settles closed windows on the way past.
31 handshake.retryDeferred(slug).catch(() => { /* the next read tries again */ });
32 const items = offers.listForParty(slug, me).map((o) => offers.queueItem(o, me));
33 items.push(...availability.lapseQueueItems(slug, me, Date.now()));
34 return collection(id, items);
35}
36
37/** Gated follows awaiting guardian approval — not built in Klonkt yet (Fase 2). */
38export function followsCollection(id) {
39 return collection(id, []);
40}
41
42/** §5.3 outbound: this ward's own follow requests, waiting for its guardians. */
43export function outgoingFollowsCollection(id, slug, me) {
44 return collection(id, outgoing.listForWard(slug).map((o) => outgoing.queueItem(o, me)));
45}
46
47/** The guardian's committed wards, with cached handle for display. */
48export function wardsCollection(id, slug) {
49 const items = relations.listWards(slug)
50 .map((r) => ({ id: r.other_uri, 'shaer:handle': r.other_handle || undefined, since: r.created_at }));
51 return collection(id, items);
52}
53
54/** The ward's guardians with their availability (§3.6.1: never public,
55 * owner-only): the real size of the safety net. Same shape as the daemon. */
56export function guardiansCollection(id, slug) {
57 const uris = relations.listGuardians(slug).map((r) => r.other_uri);
58 return collection(id, availability.statusesFor(slug, uris, Date.now()));
59}
60
61export default { offersCollection, followsCollection, outgoingFollowsCollection, wardsCollection, guardiansCollection };
Note: See TracBrowser for help on using the repository browser.