Changeset 2365630 in Klonkt


Ignore:
Timestamp:
08/14/2026 12:32:25 AM (4 weeks ago)
Author:
roboburr <roboburr@…>
Branches:
main
Children:
5a49eba
Parents:
919b82d
git-author:
Robin <roboburr@…> (08/14/2026 12:31:37 AM)
git-committer:
roboburr <roboburr@…> (08/14/2026 12:32:25 AM)
Message:

Een stempel in een vorm, anders sorteert een gesprek op schrijfwijze

Bart, 14-8: in Berichten stond een bericht van 00:30 boven een antwoord van
20:22 de avond ervoor -- op iOS en op Android allebei, en dat wees al naar hier
in plaats van naar de clients.

De unie achter een gesprek haalde haar stempel uit twee bronnen met een andere
VORM. SQLite's CURRENT_TIMESTAMP schrijft '2026-08-13 22:30:12', een binnenkomend
object draagt '2026-08-13T20:22:00Z', en soms met milliseconden erbij. Alle drie
tekst, dus vergeleken als tekst -- en op plek 10 staat een spatie tegen een T.
Een spatie is kleiner, dus sorteerde binnen dezelfde dag ALLES wat jij stuurde
vóór alles wat binnenkwam, hoe laat het ook was.

Barts eigen geval precies: 00:30 lokaal is 22:30 UTC op de 13e, en dat kwam
daarmee vóór een antwoord van 20:22Z.

strftime leest alle drie en geeft er een vorm voor terug, in UTC. Lukt dat niet,
dan blijft de rauwe waarde staan: dan is die ene rij verkeerd gesorteerd in
plaats van de hele lijst.

Dit ging ook de clients aan zonder dat iemand het zag. new Date('2026-08-13
22:30:12') leest in JavaScript als LOKALE tijd en de T-Z-vorm als UTC -- dezelfde
rij gaf dus een leeftijd die uren verschilde per vorm, en daar hangt in de hemel
de afstand tot het midden aan.

De toetsen hierboven gebruikten overal dezelfde ISO-vorm en konden hier dus niet
op falen. De drie nieuwe zetten de vormen door elkaar zoals de echte database
dat doet, op een eigen site zodat ze de tellingen van de andere niet verstoren.
Nagegaan dat ze ook echt kunnen falen: met de normalisering eruit vallen ze
alle drie om.

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

Files:
2 edited

Legend:

Unmodified
Added
Removed
  • src/services/ActivityPubService.js

    r919b82d r2365630  
    44214421// visibility 'direct'. Een publiek antwoord is geen gesprek en hoort niet als
    44224422// gezicht in de hemel.
     4423/**
     4424 * EEN STEMPEL IN EEN VORM, en dat is hier geen netheid maar de volgorde zelf.
     4425 *
     4426 * Drie vormen kwamen samen in deze unie: `2026-08-13 19:26:17` van SQLite's
     4427 * CURRENT_TIMESTAMP, `2026-08-13T18:21:57Z` uit een object, en dezelfde met
     4428 * milliseconden. Als TEKST vergeleken staat op plek 10 een spatie tegen een
     4429 * T -- en een spatie is kleiner. Dus sorteerde binnen dezelfde dag alles wat
     4430 * JIJ stuurde vóór alles wat binnenkwam, ongeacht de klok (Barts melding 14-8:
     4431 * een bericht van 00:30 stond boven een antwoord van 20:22 de avond ervoor).
     4432 *
     4433 * strftime leest alle drie en geeft er een vorm voor terug, in UTC. Lukt het
     4434 * niet, dan blijft de rauwe waarde staan -- dan is die ene rij verkeerd
     4435 * gesorteerd in plaats van de hele lijst.
     4436 *
     4437 * Dit gaat ook de client aan: `new Date('2026-08-13 19:26:17')` leest in
     4438 * JavaScript als LOKALE tijd en `...T19:26:17Z` als UTC. Dezelfde rij gaf dus
     4439 * een leeftijd die twee uur verschilde per vorm.
     4440 */
     4441const STEMPEL = (rauw) => `COALESCE(strftime('%Y-%m-%dT%H:%M:%SZ', ${rauw}), ${rauw})`;
     4442
    44234443const CONVERSATION_UNION = `
    4424   SELECT m.actor_uri AS other, COALESCE(m.published, m.created_at) AS stamp,
     4444  SELECT m.actor_uri AS other, ${STEMPEL('COALESCE(m.published, m.created_at)')} AS stamp,
    44254445         'in' AS direction, m.object_uri AS ref
    44264446    FROM ap_mentions m
    44274447   WHERE m.slug = @slug AND m.actor_uri IS NOT NULL AND m.actor_uri <> ''
    44284448  UNION ALL
    4429   SELECT j.value AS other, o.created_at AS stamp,
     4449  SELECT j.value AS other, ${STEMPEL('o.created_at')} AS stamp,
    44304450         'out' AS direction, o.id AS ref
    44314451    FROM ap_outbox o
  • test/conversations.test.js

    r919b82d r2365630  
    4141// Een PUBLIEK antwoord aan een vreemde is geen gesprek.
    4242sent('mijn-publiek', VREEMDE, '2026-08-09T12:00:00Z', 'public');
     43
     44// ── De vorm van een stempel ───────────────────────────────────────────────
     45//
     46// Barts melding (14-8): in Berichten stond een bericht van 00:30 boven een
     47// antwoord van 20:22 de avond ervoor, op iOS en op Android. De oorzaak zat
     48// niet in de clients maar hier: de twee poten van de unie leverden een ANDERE
     49// VORM. SQLite's CURRENT_TIMESTAMP schrijft '2026-08-13 22:30:12', een object
     50// draagt '2026-08-13T20:22:00Z', en soms met milliseconden erbij. Als tekst
     51// vergeleken staat op plek 10 een spatie tegen een T, en een spatie is kleiner
     52// -- dus stond binnen dezelfde dag alles wat jij stuurde vóór alles wat
     53// binnenkwam.
     54//
     55// De toetsen hierboven gebruikten overal dezelfde ISO-vorm en konden hier dus
     56// niet op falen. Deze zet ze door elkaar, precies zoals de echte database.
     57//
     58// Eigen site, want de toetsen hierboven tellen ALLE gesprekken van 'kind' en
     59// een gezicht erbij zou ze laten vallen om een reden die er niets mee te maken
     60// heeft.
     61db.prepare('INSERT INTO sites (id, slug, title, owner_id) VALUES (?,?,?,?)').run('s2', 'buur', 'Buur', 'u1');
     62const BUURMAN = 'https://elders/u/buurman';
     63const kwam = (uri, stamp) =>
     64  db.prepare(`INSERT INTO ap_mentions (slug, object_uri, actor_uri, actor_name, content, published)
     65              VALUES ('buur', ?, ?, 'buurman', '<p>hoi</p>', ?)`).run(uri, BUURMAN, stamp);
     66const ging = (id, stamp) =>
     67  db.prepare(`INSERT INTO ap_outbox (id, site_slug, post_id, to_actor, to_actors, content, visibility, created_at)
     68              VALUES (?, 'buur', 'p1', ?, ?, '<p>terug</p>', 'direct', ?)`)
     69    .run(id, BUURMAN, JSON.stringify([BUURMAN]), stamp);
     70
     71kwam('https://elders/n/buur-mid', '2026-08-13T14:17:45.000Z');
     72ging('mijn-buur-1', '2026-08-13 16:16:00');
     73kwam('https://elders/n/buur-avond', '2026-08-13T20:22:00Z');
     74ging('mijn-buur-2', '2026-08-13 22:30:12');
     75
     76test('vormen door elkaar sorteren op de KLOK, niet op hun schrijfwijze', () => {
     77  const talk = AP.conversationHistory('buur', BUURMAN, { limit: 60 });
     78  assert.deepEqual(talk.rows.map((r) => r.ref), [
     79    'mijn-buur-2',                      // 22:30
     80    'https://elders/n/buur-avond',      // 20:22
     81    'mijn-buur-1',                      // 16:16
     82    'https://elders/n/buur-mid',        // 14:17
     83  ], 'nieuwste eerst, om en om -- niet eerst al het uitgaande');
     84});
     85
     86test('de stempel komt er in EEN vorm uit, want de client rekent ermee', () => {
     87  // new Date('2026-08-13 22:30:12') leest in JavaScript als LOKALE tijd en
     88  // '...T22:30:12Z' als UTC. Dezelfde rij gaf dus een leeftijd die per vorm
     89  // uren verschilde, en daar hangt in de hemel de afstand tot het midden aan.
     90  const talk = AP.conversationHistory('buur', BUURMAN, { limit: 60 });
     91  for (const r of talk.rows) {
     92    assert.match(r.stamp, /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/, `stempel ${r.stamp}`);
     93  }
     94});
     95
     96test('de cursor blijft werken over de vormen heen', () => {
     97  const eerste = AP.conversationHistory('buur', BUURMAN, { limit: 2 });
     98  assert.equal(eerste.more, true);
     99  const tweede = AP.conversationHistory('buur', BUURMAN, { limit: 2, before: eerste.oldest });
     100  assert.deepEqual(
     101    [...eerste.rows, ...tweede.rows].map((r) => r.ref),
     102    ['mijn-buur-2', 'https://elders/n/buur-avond', 'mijn-buur-1', 'https://elders/n/buur-mid'],
     103    'twee pagina\'s samen zijn hetzelfde gesprek',
     104  );
     105});
    43106
    44107test('de oude lezing verliest tante achter een druk gesprek -- dat is de bug', () => {
Note: See TracChangeset for help on using the changeset viewer.