source: Klonkt/src/services/guardianship/queues.js@ 3d882bd

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

Een handshake blijft een week open, en faalt daarna onder zijn eigen naam

§4.2 leunt erop dat het uitstel begrensd is: als het venster sluit met de
controle nog onbeslist, faalt de beslissing dicht. Alleen had een
guardianship-offer in Klonkt helemaal geen venster, dus "uitgesteld" was
"voor altijd".

Nu een week. Lang genoeg dat niemand wordt opgejaagd — er moeten een ward, een
kandidaat en alle bestaande guardians antwoorden, en dat zijn mensen — en kort
genoeg dat een vergeten aanbod niet een maand in de wachtrij van een kind staat
alsof het nog een keuze is.

Twee eindtoestanden, want het zijn twee verschillende feiten:
'expired' (niemand heeft geantwoord; zegt niets over de kandidaat) en
'unverified' (iedereen heeft geantwoord, maar de kandidaat was nooit op te
halen). Geen van beide is 'void': de partijen mag niet verteld worden dat de
kandidaat geweigerd is, want dat is niet gebeurd — er heeft alleen nooit iemand
kunnen kijken.

Vervallen gebeurt bij het lezen, zoals een lapse dat ook doet: geen sweeper die
niemand draait. En het lezen van de wachtrij is meteen het moment waarop een
uitgestelde commit opnieuw wordt geprobeerd (§4.2 SHOULD) — nodig, want de
laatste Accept kan al binnen zijn en dan port niemand er ooit nog aan. Niet
awaited: een poll toont wat nu waar is.

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

  • Property mode set to 100644
File size: 2.6 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 handshake from './handshake.js';
15
16const collection = (id, items) => ({
17 id, type: 'OrderedCollection', totalItems: items.length, orderedItems: items,
18});
19
20/** Pending offers where the local site is a party, each with its accept
21 * tally. The same collection carries the running lapses (§3.6.3) this
22 * account is a party to, exactly as the daemon serves them, so the Shaer
23 * clients render both without a second fetch. */
24export function offersCollection(id, slug, me) {
25 // §4.2: a handshake whose candidate could not be dereferenced is deferred,
26 // not decided, and the last Accept may already have landed — so nothing else
27 // would ever retry it. This poll is the schedule. Not awaited: the read
28 // answers with what is true now, and a retry that succeeds surfaces in the
29 // next one. `listForParty` settles closed windows on the way past.
30 handshake.retryDeferred(slug).catch(() => { /* the next read tries again */ });
31 const items = offers.listForParty(slug, me).map((o) => offers.queueItem(o, me));
32 items.push(...availability.lapseQueueItems(slug, me, Date.now()));
33 return collection(id, items);
34}
35
36/** Gated follows awaiting guardian approval — not built in Klonkt yet (Fase 2). */
37export function followsCollection(id) {
38 return collection(id, []);
39}
40
41/** The guardian's committed wards, with cached handle for display. */
42export function wardsCollection(id, slug) {
43 const items = relations.listWards(slug)
44 .map((r) => ({ id: r.other_uri, 'shaer:handle': r.other_handle || undefined, since: r.created_at }));
45 return collection(id, items);
46}
47
48/** The ward's guardians with their availability (§3.6.1: never public,
49 * owner-only): the real size of the safety net. Same shape as the daemon. */
50export function guardiansCollection(id, slug) {
51 const uris = relations.listGuardians(slug).map((r) => r.other_uri);
52 return collection(id, availability.statusesFor(slug, uris, Date.now()));
53}
54
55export default { offersCollection, followsCollection, wardsCollection, guardiansCollection };
Note: See TracBrowser for help on using the repository browser.