fix(live): der Client stritt mit sich selbst (D76-Nachtrag 9)

Gemeldet: zwei Browser am selben Dokument, einen Knoten zuklappen, und es
kommt "Someone changed the same lines. Whose version should win?".

Der Feed beantwortet "was ist seit Version N geschehen" - und wer da
mitgeschrieben hat, steht nicht in der Frage. Er liefert also die EIGENE
Aenderung zurueck, und wacht er im Moment des eigenen Sendens auf, kommt sie
an, bevor die Antwort darauf da ist. Die Schattenkopie steht dann noch auf dem
Stand davor: Der Client haelt die eigene Aenderung fuer fremd, sieht sie sich
mit dem eigenen Text ueberschneiden und fragt. Die Erkennung hatte recht,
falsch war nur, wen sie fuer den anderen hielt. Der zweite Browser ist dafuer
gar nicht noetig; das Falten ist nur die kuerzeste Geste, die eine ganze Zeile
aendert.

Auf localhost liegen PATCH- und Feed-Antwort 7 ms auseinander und die
PATCH-Antwort gewinnt - der Fehler tritt dort nie auf. Reproduziert mit im
Client um 500 ms verzoegerter PATCH-Antwort (eine Reihenfolge, die uebers Netz
jederzeit auftritt): PATCH an 200 / FEED an 200 / KONFLIKT-BANNER.

Behoben in feedAction() (live.js) - dort steht ohnehin, wann eine Feed-Antwort
angewendet werden darf; Gegenprobe: Sperre entfernt => genau die zwei neuen
Zusicherungen fallen. Dieselbe Sperre gehoert in die runFeed-Schleife, sonst
fragt sie sofort wieder und dreht eine enge Runde uebers Netz.

Dabei gefunden: pushLive() las seine Basis erst NACH dem await und nahm damit
an, dass sich dazwischen nichts aendert - der Feed brach genau die Annahme und
haette die eigene Aenderung ein zweites Mal aufgerechnet. Jetzt vorher
festgehalten.

Nachgemessen: Falten erzeugt kein Banner mehr, fremde Aenderungen kommen
weiterhin an, und der ECHTE Konflikt wird weiterhin erkannt (A haelt
ungesendeten Text auf Zeile 1, B aendert dieselbe Zeile). 525 Tests.
This commit is contained in:
mhoennig
2026-08-26 20:12:42 +02:00
parent ac6df1bb6b
commit e19a119966
6 changed files with 127 additions and 8 deletions
+11
View File
@@ -129,6 +129,17 @@ verworfene Elemente. Quelle sind ES-Module unter `src/`; `index.html` ist der
**Konflikt entsteht beim Tippen**, nicht erst beim Senden — der Server kennt
den ungesendeten Text nicht. Fremde Änderungen werden bewusst **nicht**
undo-fähig eingespielt.
**Der Feed liefert die EIGENE Änderung zurück** (D76-Nachtrag 9): Er
beantwortet „was ist seit Version N geschehen", und wer mitgeschrieben hat,
steht nicht in der Frage. Wacht er im Moment des eigenen Sendens auf, hielte
der Client sich selbst für den anderen und fragte, wessen Fassung gelten soll.
Deshalb `busy` an **zwei** Stellen: in `feedAction()` (Antwort auslassen) und
in der `runFeed`-Schleife (gar nicht erst fragen — sonst dreht sie eine enge
Runde übers Netz). `pushLive()` hält seine Basis **vor** dem `await` fest;
hinterher aus `liveState` gelesen nähme sie an, dass sich währenddessen
nichts ändert. Auf localhost liegen PATCH- und Feed-Antwort **7 ms**
auseinander und die PATCH-Antwort gewinnt — wer das prüfen will, muss die
PATCH-Antwort im Client verzögern, sonst meldet die Messung „geht doch".
- Zustand wird im `localStorage` gehalten (Server-Dokumente ausgenommen): `werkbaum-lang`
(Sprache), `werkbaum-docs` (JSON-Array der Dokumente `[{id,name,text}]`),
`werkbaum-active` (id des aktiven Dokuments), `werkbaum-src` (Spiegel des