aa8842e72e3982e38b9170caed5b988cfbc491b0
14
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
6e46a08543 |
Portnummern werden abgeleitet, nicht mehr vergeben
Gemessen: 165 Pruefdateien, 129 verschiedene Nummern -- NEUNZEHN
doppelt, vier davon dreifach. Niemand hatte das gewollt; jede neue
Pruefung wurde von einer vorhandenen abgeschrieben, und die Nummer kam
mit. Meine eigene Notiz sagte "sieben" -- auch eine Bestandsliste
altert.
Der Waechter faengt den Schaden ab, aber er kann nur melden, was schon
passiert ist: Zwei Pruefungen mit derselben Nummer koennen nie
gleichzeitig laufen, und ein liegengebliebener Prozess der einen laesst
die andere abbrechen mit einer Meldung, die wie ein Befund aussieht.
Genau das ist mir am 20.09. zweimal passiert.
Jetzt leitet jede Datei ihre Nummer aus ihrer STELLE IM ALPHABET ab
(eigenerPort in helfer-port.mjs), zwei je Datei. Nicht ueber eine
Pruefsumme: Bei 165 Namen in 4900 Nummern waeren nach dem
Geburtstagsproblem rund DREI Zusammenstoesse zu erwarten -- ein Hash
tauscht eine sichtbare Doppelung gegen eine unsichtbare. Die Stelle im
Alphabet ist eindeutig von der Bauart her.
--- ZWEI FEHLER AUF DEM WEG, BEIDE LEHRREICH -------------------------
1. DER ERSTE VERSUCH WAR GRUEN UND KAPUTT. Ersetzt wurde mit einem
Muster: "([^"]*4231[^"]*)". Das hielt
{ host: "127.0.0.1", port: 4231, path: "/404.html" }
fuer eine Zeichenkette -- ein Muster kann eine oeffnende nicht von
einer schliessenden Anfuehrung unterscheiden. Heraus kam
{ host: "127.0.0.1`, port: ${PORT}, path: `/404.html" }
also GUELTIGER Code ohne port-Feld. `node --check` sagte gruen fuer
alle 164 Dateien. Aufgefallen ist es erst, weil ich vier Vertreter
gegen eine vorher gemessene Grundlinie laufen liess: pruef-schranke
39/0 vorher, 38/1 nachher.
Alles zurueckgenommen und mit einem Zerleger neu gemacht, der weiss,
ob eine Stelle Code, Zeichenkette, Vorlage, Kommentar oder
regulaerer Ausdruck ist. In pruef-ics stand die Nummer in einem
regulaeren Ausdruck -- der wird jetzt gebaut statt hingeschrieben.
2. EIN MODUL, DAS BEIM IMPORTIEREN ARBEITET, IST EINE FALLE. Der
zweite Durchgang importierte den ersten, um seine Mechanik zu
benutzen -- und fuehrte dessen Hauptlauf gleich mit aus. Die
zweiten Nummern wurden dadurch als erste behandelt, zwei Aufrufe
bekamen dieselbe Nummer, und in pruef-content stand `const PORT`
zweimal.
--- WAS DAS DAUERHAFT HAELT -----------------------------------------
pruef-portnummern.mjs (neu, 8 Pruefungen) fragt nicht "welche Nummern
sind doppelt", sondern "wer traegt ueberhaupt noch eine von Hand ein"
und "wer startet einen Server, ohne seine Nummer abzuleiten". Die
zweite Frage hat sofort etwas gefunden, das in KEINER Doppelungsliste
stand: pruef-push-weg belegte 4341 und 4342, rief den Waechter aber
gar nicht auf -- dieselbe Nummer wie pruef-agentur. Eine Liste zeigt
nur, was auf ihr steht.
Mit Gegenprobe: Eine unbekannte Datei bekommt keine geratene Nummer,
sondern einen Abbruch, und eine dritte Nummer je Datei gibt es nicht.
--- NACHGEMESSEN ----------------------------------------------------
Sechzehn Pruefungen gegen ihre vorher gemessene Grundlinie, je eine
Vertreterin jeder umgestellten Bauweise (eine Nummer, zwei Nummern,
Nummer in einer Zeichenkette, in einer Vorlage, in einem regulaeren
Ausdruck, dynamische Einfuhr, Nachtlauf mit zwei Laeufen):
crew-adresse 132/0 · schranke 39/0 · content 45/0 · entwicklung 46/0
anruf 127/0 · ics 37/0 · push-weg 20/0 · arten 28/0 · video 67/0
kanalzeile 14/0 · agentur 62/0 · spicy 83/0 · creator-anlegen 50/0
push-ziel 10/0 · portnummern 8/0
Alle exakt wie vorher. Zwei waren schon vorher rot und sind es
unveraendert geblieben (crew-wand-bild 41/4, teilen 14/3) -- per
`git stash` belegt, nicht angenommen.
Co-Authored-By: Claude Opus 5 <[email protected]>
|
||
|
|
b0e7542507 |
Der Anrufkasten zeigt, WER dran ist -- nicht dreimal "Verbunden"
Filipe, zu Screen 1: "wenn ich anrufe soll das auch viel besser geiler und spezieller aussehen aber es soll nicht raushaengen." Erst gemessen: Es haengt nichts raus. Der Kasten ist 374 px am Handy, 340 am Rechner, 72 Teile geprueft, kein einziges ueber dem Rand. Die Optik war also gar nicht der Mangel -- zwei andere Dinge waren es. 1. DER NAME DES ANRUFERS GING IMMER VERLOREN. `wer(raumName || "Anruf")` lief VOR `kastenBauen()`. Das Element #anruf-wer gab es zu dem Zeitpunkt noch nicht, die Zuweisung lief ins Leere, und oben stand bei jedem Anruf das Wort "Anruf". Die Reihenfolge ist umgedreht: erst bauen, dann beschriften. 2. IN EINEM ANRUF OHNE VIDEO STAND JE PERSON DAS WORT "Verbunden". Bei dreien dreimal dasselbe Wort -- und man sah nicht, WER dabei war. In einer Gruppe ist genau das die Frage: Ist Kessi schon drin? Jetzt steht dort ein Kreis mit den Anfangsbuchstaben und der echte Name; waehrend es klingelt ein wartendes Gesicht mit dem Namen dessen, den man anruft. Vorher war die Flaeche in dieser Sekunde leer, und ein leerer Kasten mit "Es klingelt ..." sieht aus wie einer, der haengt -- ausgerechnet im unsichersten Moment. Der Name kommt NICHT aus einer Abfrage. Wer anruft, bekannt beim Start nur sich selbst; alle weiteren kommen ausschliesslich ueber das Ereignis "dabei", und genau dort fehlte das Einsammeln. Deshalb sah der ANRUFER unter jedem Kreis "Jemand" -- die Angerufenen nicht. UND DIE PRUEFUNG WAR ZU NACHSICHTIG. Sie verlangte einen Namen "laenger als ein Zeichen"; der Rueckfalltext "Jemand" ist sechs Zeichen lang und kam damit durch. Gruen, waehrend der Hauptfall kaputt war. Die zweite Fassung verglich gegen eine abgeschriebene Namensliste -- die nannte "Jonas, Luna, Mara, Pat", angelegt werden aber "Filipe, Rieke, Kessi, Tili", und der Kommentar daneben behauptete schon da, sie sei abgeleitet. Jetzt ist sie es wirklich: Die Namen werden beim Anlegen eingesammelt, und geprueft wird, dass jeder GENAU DIE BEIDEN ANDEREN sieht -- nicht "irgendeinen echten Namen", denn der eigene Name im eigenen Kasten waere auch einer. pruef-anruf: 127 Pruefungen, 0 Fehler. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
c59517cb23 |
Ein Anruf faengt leise an
Filipe: "uebrigens soll man auswaehlen koennen ob man lautsprecher will oder nicht bitte. es soll ohne lautsprecher anfangen das gespraech und nur wenn man drauf klickt soll es laut werden ... es soll nicht raushaengen oder so, das ist nicht cool." DER GRUND IST DER STREAM. Ein Anruf, der beim Annehmen sofort aus den Lautsprechern kommt, ist im schlimmsten Fall fuer alle Zuschauer zu hoeren, bevor irgendjemand reagieren kann. Das laesst sich nicht zurueckholen. HIER STAND `hoeren = true` mit der Begruendung: "Wer den Ton abgestellt hat, will ihn nicht beim naechsten Gespraech wieder an." Das war richtig gedacht und ist jetzt umgedreht -- aus demselben Grund, nur in die andere Richtung: Der Zustand soll NICHT ueberdauern. Eine Entscheidung von vorhin darf nicht fuer ein Gespraech gelten, von dem man noch nichts wusste. Zurueckgesetzt wird an BEIDEN Wegen, beim Anrufen und beim Rangehen -- einer allein waere die Haelfte, und die andere Haelfte faellt niemandem auf, bis es einmal zu laut war. UND MAN SIEHT ES. Leise anfangen ohne Hinweis waere die naechste Sackgasse: "ich hoere nichts" ohne Grund und ohne Weg. Ein Balken sagt es, solange es gilt, und verschwindet in dem Moment, in dem man den Ton anmacht. Er ist SELBST der Knopf -- wer liest "tippen, um den Ton anzumachen", will genau das tun. Gedaempftes Bernstein statt Alarmrot: Es ist kein Fehler, sondern ein Zustand. DABEI EINEN EIGENEN FEHLER GEFANGEN: Der Balken fragte `!anruf` -- und diese Variable wird erst gesetzt, NACHDEM der Kasten aufgeht. Beim Aufbauen blieb er deshalb versteckt, und man sass in einem stummen Gespraech ohne einen Satz dazu. Genau der Zustand, den er verhindern soll. Jetzt haengt er am sichtbaren Kasten. DIE PRUEFUNG WURDE ROT und hat damit ihre Arbeit getan -- sie hielt das alte Verhalten fest. Umgedreht, nicht gestrichen: Die wichtigste Falle (ueberlebt der Schalter das Neuzeichnen, wenn jemand auflegt?) bleibt, nur der erwartete Zustand hat sich gedreht. GEPRUEFT: pruef-anruf 117/0 (war 114) -- drei Aussagen mehr, darunter dass der Hinweis da ist, 44 px hoch und im richtigen Moment verschwindet. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
d6df590224 |
Telefonieren ging nur ueber UDP -- und die Seite sprang immer hoch
=== 1. WARUM TELEFONIEREN NICHT GING ===
Filipe: "es klingelt erscheint auch alles bei jedem aber telefonieren
klappt immer noch nicht."
GEMESSEN, BEVOR ETWAS GEAENDERT WURDE -- und alles war gruen:
coturn laeuft aktiv
STUN von aussen antwortet, nennt meine oeffentliche Adresse
echte Zuteilung von aussen 7/7, Testpaket kam an (Port 49183)
Dreier-Anruf im Prueflauf verbindet, alle hoeren alle
Vier Messungen, vier Mal in Ordnung -- und das Telefon ging trotzdem
nicht. Der Grund: Der Prueflauf faehrt BEIDE Seiten auf demselben
Rechner. Dort finden sie sich ueber die direkte Adresse, und die
Vermittlung wird nie gebraucht. Draussen sitzen sie in verschiedenen
Netzen.
DER FUND: Eingetragen war eine einzige Adresse --
turn:159.195.212.167:3478
Ein `turn:` OHNE `?transport=` heisst UDP, und nur UDP. Nachgemessen
hoert coturn aber auf beidem: 3478/UDP offen, 3478/TCP offen (5349/TLS
ist zu). Angeboten wurde nur der halbe Server.
Wer in einem Netz sitzt, das UDP nach draussen sperrt -- Mobilfunk mit
strengem Profil, Gast-WLAN, Firmennetz --, bekam damit KEINEN
Vermittlungsweg. Es klingelt (das laeuft ueber die Website, also ueber
443), und danach passiert nichts. Genau das gemeldete Bild.
Aus einem Eintrag werden jetzt drei Wege:
stun:host:port die eigene Adresse finden, ohne Vermittlung
turn:host:port?transport=udp der schnelle Weg
turn:host:port?transport=tcp der Weg durch fast jede Sperre
ABGELEITET, NICHT EINGETRAGEN: Drei Zeilen von Hand waeren drei
Stellen, an die beim naechsten Serverumzug jemand denken muesste --
die abgeschriebene Liste, die hier schon zweimal teuer war. Ein
Eintrag MIT `?transport=` bleibt unangetastet, ein fremder Dienst mit
eigenem Passwort sowieso.
pruef-turn-wege (neu, 15/0) sichert beides: dass jeder Weg herauskommt
UND dass jeder turn:-Weg Zugangsdaten traegt. Das Zweite ist das
wichtigere -- auffaechern ohne anmelden haette den Fehler nur
verschoben.
=== 2. DIE SEITE SPRANG BEIM ZURUECKGEHEN IMMER HOCH ===
Filipe: "das nervt man muss dan immer wieder runter scrollen bis man
da ist wo man vorher war."
Der Browser versucht es sogar -- scrollRestoration steht ab Werk auf
"auto". Nur: Jede Seite hier kommt fast leer an und holt ihren Inhalt
danach per Abruf. In dem Moment, in dem der Browser die alte Position
wiederherstellen will, ist das Dokument ein paar hundert Pixel hoch.
Er kann nicht auf Zeile 900 springen, die es noch nicht gibt -- und er
versucht es kein zweites Mal.
Jetzt macht es kopf.js selbst (gilt damit auf allen 21 Seiten): Stelle
merken beim Verlassen, beim Oeffnen zurueckholen und jeden Bildaufbau
lang versuchen, bis das Dokument hoch genug ist. Nach drei Sekunden
wird aufgegeben.
Drei Dinge sind Absicht:
- NUR beim Zurueckgehen, nicht bei jedem Oeffnen. Wer eine Kachel
anklickt, will oben anfangen.
- WER SELBST SCROLLT, GEWINNT. Rad, Wisch oder Taste beenden das
Nachspringen sofort.
- Nach einer Stunde vergessen -- auf Zeile 900 zu landen, weil man
gestern dort war, ist keine Hilfe.
=== 3. DIE LISTE IST NACH ROLLE GETRENNT ===
Filipe: "die rechte hand rolle immer zuerst und dan die modis. die
sollen auch schoen getrennt sein und verschieden aussehen also die
rechte hand viel spezieller."
Die Reihenfolge macht der Server ueber ROLLEN_SORTIERUNG -- dieselbe
Konstante wie ueberall sonst im Haus, nicht eine zweite. Eine
Sortierung nach ROLLE ist ausdruecklich keine Rangliste: Sie sagt
nichts darueber, wie gut jemand ist, nur welche Aufgabe er hat.
Deshalb steht ueber jeder Gruppe ein Satz und keine Zahl.
"Spezieller" heisst hier Material, nicht Groesse: eigener Farbton als
Kante und Schimmer, hellere Flaeche, kraeftigerer Name. Beide Karten
sind GLEICH GROSS und tragen dieselben Zeilen -- zwei Sorten Aufgabe,
keine Rangfolge. Im Kontrastmodus traegt eine doppelte Kante die
Unterscheidung, weil dort keine Farbe mehr wirkt.
=== 4. DER ANRUFKASTEN ===
Vorher: vier gleich aussehende Pillen nebeneinander, darunter eine
Geraeteauswahl, die das breiteste Element war. Kein Name, keine
Ordnung, und der einzige Knopf mit Folgen sah aus wie die anderen.
- MAN SAH NICHT, MIT WEM. Der wichtigste Satz eines Telefonats fehlte.
Der Chat kennt den Namen und gibt ihn jetzt mit; wer rangeht, sieht
den Anrufer.
- "MIKRO AN" WAR ZWEIDEUTIG -- "ist an" oder "schalt an"? Wer falsch
raet, sitzt stumm da. Jetzt traegt ein durchgestrichenes Zeichen
den Zustand, das Wort nur die Sache. aria-pressed bleibt die
Wahrheit, auch fuer Vorleseprogramme.
- DIE GERAETEAUSWAHL liegt hinter einem kleinen Knopf, der nur
erscheint, wenn es ueberhaupt etwas zu waehlen gibt.
- AUFLEGEN ist als einziger Knopf farbig und steht immer rechts.
- Ein ruhiger Puls atmet, solange die Verbindung aufbaut, und haelt
an, sobald sie steht. Bei prefers-reduced-motion steht er still.
=== NEBENBEFUND, DEN DIE PRUEFUNG GEFUNDEN HAT ===
pruef-anruf wurde durch die Auffaecherung an vier Stellen rot: Sie
griff `adressen[0]` und erwartete dort Zugangsdaten. Das ist jetzt der
STUN-Weg, und der hat richtigerweise keine. Gesucht wird nicht mehr
nach PLATZ, sondern nach ART -- ein Index ist eine Annahme darueber,
wie die Liste aussieht, und die hat sich gerade geaendert.
Dazu angepasst: Die Pruefung "die Uhr laeuft" verlangte, dass die Uhr
schon beim Klingeln zaehlt. Seit heute frueh beginnt sie beim
Rangehen (sonst standen im Kasten und im Chat zwei verschiedene
Zahlen). Sie prueft jetzt das Gegenteil -- statt sie zu streichen,
denn eine Zeile weniger haette den Fehler beim naechsten Umbau wieder
durchgelassen.
GEMESSEN: pruef-anruf 114/0 (vorher 111 -- drei MEHR, weil die
Auffaecherung mitgeprueft wird), pruef-turn-wege 15/0,
pruef-meldungen 8/0, pruef-css-klassen ALLES IN ORDNUNG,
pruef-rechtetafel 19/0.
Co-Authored-By: Claude Opus 5 <[email protected]>
|
||
|
|
d766be57b6 |
Anruf: wer auflegt, hinterlaesst jetzt auch eine Spur
Filipe, 00:30 Uhr: „hab angerufen aufgelegt und da steht immer noch nichts im chat vom anruf...." Im Protokoll: vier Anrufe, je drei bis zehn Sekunden, kein einziger Eintrag im Gespraech. Und mein Denkfehler dahinter war einfach: „Verpasster Anruf" stand NUR in `aufraeumen()` -- also erst, wenn ein Anruf nach zwei Minuten von selbst verfaellt. Wer vorher auflegt, loeschte ihn spurlos. Abschnitt 5e hat damit genau den Weg geprueft, den niemand geht: warten, bis es von allein aufhoert. Im Alltag legt man auf. Fuer die andere Seite ist ein Anrufer, der aufgibt, ERST RECHT ein verpasster Anruf. UND EINE ZWEITE FALLE, die die erste verdeckt hat: `verpasstVermerken` nahm den Anrufer aus der Runde der Anwesenden (`a.wer`). Beim Verfallen steht er noch darin -- beim AUFLEGEN ist sie leer. Die Zeile waere also auch dann ausgefallen, wenn der Aufruf an der richtigen Stelle gestanden haette. Jetzt kommt er aus `a.anrufer`, wie beim gefuehrten Anruf auch. Zwei Fehler, die einander gedeckt haben, und beide hat dieselbe Pruefluecke durchgelassen: Ich hatte den Ablauf geprueft, den ich gebaut hatte, nicht den, den ein Mensch geht. Geprueft: pruef-anruf 106 -> 111. Der neue Abschnitt ruft an, wartet kurz, legt auf -- und misst, dass genau EINE Zeile entsteht, dass sie „verpasst" sagt, dass der Anrufer daran steht und dass spaeteres Aufraeumen keine zweite schreibt. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
00459f47c6 |
Anruf: "Anruf · 3:42" steht jetzt im Chat, nicht nur der verpasste
Filipe: „im chat soll auch stehen verpasster anruf oder anruf gehabt
und wie lange."
Die zweite Haelfte. Ein verpasster Anruf steht seit heute drin -- ein
GEFUEHRTER stand nirgends. Nach dem Auflegen war das Gespraech spurlos
weg.
Tage spaeter ist die Frage aber nicht „hat er angerufen", sondern
„haben wir das besprochen oder nur kurz telefoniert?". Drei Minuten
sind ein Gespraech, zwoelf Sekunden sind ein Verwaehlen. Eine Zeile
ohne Zahl beantwortet das nicht.
GEZAEHLT AB DEM RANGEHEN, nicht ab dem Klingeln. Sonst staende bei
jedem Anruf eine halbe Minute zu viel darin -- die Zeit, in der das
Telefon nur laeutete. Dafuer merkt sich der laufende Anruf jetzt zwei
Dinge zusaetzlich:
* `gespraechSeit` -- gesetzt, wenn der Zweite dazukommt.
* `anrufer` -- wer begonnen hat. `wer` ist die Runde der
gerade Anwesenden; sie leert sich, und am Ende
laesst sich daraus nicht mehr ablesen, von wem
der Anruf ausging. Die Zeile steht bei ihm, wie
bei jedem Telefon.
Unter einer Minute steht die Sekundenzahl („7 Sekunden"), darueber
Minuten („3:42 Minuten") -- „0:07" liest sich umstaendlicher, als es
ist.
Geprueft: pruef-anruf 101 -> 106. Der neue Abschnitt fuehrt einen
echten Anruf: anrufen, rangehen, kurz warten, beide auflegen -- und
misst dann, dass genau EINE Zeile entsteht, dass sie „Anruf" sagt und
nicht „verpasst", und dass die Dauer in SEKUNDEN steht. Stuende dort
die Zeit seit dem Klingeln, waeren es Minuten.
Co-Authored-By: Claude Opus 5 <[email protected]>
|
||
|
|
c87c396ef5 |
Anruf: es klingelt jetzt wirklich -- vorher gab es gar keinen Ton
Filipe: „ist das normal dass es nicht mal klingelt wenn man anruft?"
Nein. Und der Grund war so einfach wie peinlich: Es gab ueberhaupt
keinen Ton. Weder beim Anrufer noch beim Angerufenen. Der Anruf zeigte
einen Kasten auf dem Bildschirm, sonst nichts -- wer nicht zufaellig
hinsah, merkte nichts, und wer anrief, wusste nie, ob ueberhaupt etwas
passiert.
Damit erklaert sich auch, warum heute Abend alles „kaputt" wirkte,
obwohl Verbindung, Raum, Anmeldung und Code stimmten: Ein stummes
Telefon ist von einem defekten nicht zu unterscheiden.
ZWEI TOENE, JE NACH SEITE:
Angerufener zwei weiche Toene (880/660 Hz), dann Pause -- alle
2,4 Sekunden, dazu Vibration, wo das Geraet sie kann.
Anrufer ein leiseres, langsameres Freizeichen (440 Hz). Ein
stiller Kasten mit laufender Uhr sagt nur, DASS etwas
passiert; ein Freizeichen sagt, was.
OHNE TONDATEI. Ein .mp3 waere eine Datei mehr im Netz, eine
Lizenzfrage und ein Ladefehler, der genau dann auffaellt, wenn man ihn
braucht. Zwei Sinustoene aus dem Browser reichen. Sanft ein- und
ausgeblendet, weil ein hart geschalteter Sinus knackt -- und das
Knacken ist das Unangenehme daran.
Der Ton hoert auf, sobald jemand rangeht (`dabei`), sobald die
Verbindung steht und beim Auflegen. Drei Stellen, weil ein
Freizeichen, das waehrend des Gespraechs weiterlaeuft, das ist, was
man an Telefonanlagen hasst.
EHRLICH DAZU: Browser lassen Ton erst zu, wenn jemand auf der Seite
etwas angeklickt hat. Wer die Seite gerade erst geoeffnet hat, hoert
unter Umstaenden nichts -- dann bleibt die Benachrichtigung des
Geraets der Weg. Das ist eine Regel des Browsers, keine Entscheidung
von uns.
Geprueft: pruef-anruf 100 -> 101. Gemessen wird am Oszillator, nicht
am Lautsprecher: Ob wirklich etwas zu hoeren ist, haengt an Geraet und
Lautstaerke -- dass die Seite Toene ERZEUGT, laesst sich messen.
Gegenprobe (Freizeichen ausgebaut): „0 Toene erzeugt", rot.
Co-Authored-By: Claude Opus 5 <[email protected]>
|
||
|
|
31906e9216 |
Pruefung berichtigt: die kurze Klingeldauer galt fuer den ganzen Lauf
Der Commit davor ging mit EINEM roten Test hinaus -- ich habe das Ergebnis nicht angesehen, weil Pruefung und Commit in derselben Kette liefen. Das war der Fehler, nicht der rote Test selbst. Die Ursache war die Pruefung, nicht der Code: ANRUF_KLINGELT_SEKUNDEN stand auf 2, und das gilt fuer den GANZEN Lauf. Im Browser-Teil vergehen zwischen "anrufen" und "der Server kennt den Anruf" mehrere Sekunden -- dort war er damit schon verfallen. Jetzt zehn Sekunden: lang genug fuer den Browser, kurz genug, dass Abschnitt 5e nicht zur Geduldsprobe wird. 100 Pruefungen, 0 Fehler. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
e43fe97550 |
Anruf: zwei Minuten Zeit -- und ein verpasster Anruf bleibt sichtbar
Filipe: „das mit dem anruf klappt nicht … es geeeeeht einfach nicht." NACHGEMESSEN WAR SERVERSEITIG ALLES IN ORDNUNG: Der Anruf kam an (Protokoll), der Raum stimmte (Dogfather + VanVan), sie war angemeldet (Sitzung bis morgen frueh), sie hat ein Geraet fuer Benachrichtigungen, und der neue Code lief. Trotzdem ging es nicht -- und zwar aus zwei Gruenden, die beide nichts mit Technik zu tun haben, sondern mit Zeit und Sichtbarkeit. 1. 45 SEKUNDEN SIND KEIN KLINGELN. Bei einem Telefon hebt man ab. Hier muss in dieselben Sekunden: Benachrichtigung zustellen, bemerken, Handy entsperren, antippen, App laden, Anmeldung pruefen. Das reicht, wenn man das Geraet in der Hand haelt -- und sonst nie. Jetzt zwei Minuten: lang genug, um aus der Tasche zu kommen, kurz genug, dass niemand vor einem Anruf sitzt, den es nicht mehr gibt. 2. UND DANACH WAR ES, ALS WAERE NIE ETWAS GEWESEN. Ein verpasster Anruf verschwand lautlos: keine Zeile, kein Hinweis, nicht einmal, DASS jemand angerufen hat. Genau das fuehlt sich an wie „geht nicht", auch wenn alles funktioniert hat. Jedes Telefon der Welt zeigt einen verpassten Anruf; jetzt steht er als Zeile im Gespraech, beim Anrufer, mit Uhrzeit. Geprueft: pruef-anruf 95 -> 100. Der neue Abschnitt misst den echten Ablauf (anrufen, niemand geht ran, Zeile erscheint) und dass sie NUR EINMAL erscheint -- `aufraeumen()` laeuft bei jeder Anfrage mit. Dafuer ist die Klingeldauer ueber die Umgebung einstellbar: Eine Pruefung, die zwei Minuten wartet, wird abgeschaltet, und dann waere der verpasste Anruf wieder ungeprueft. Im Betrieb steht die Variable nirgends. Und noch ein eigener Messfehler: Die Pruefung las zuerst /api/chat/raum/:id -- die Route heisst /api/chat/raeume/:id/nachrichten. Sie meldete „0 Nachrichten, vorher wie nachher" und sah damit aus wie ein Befund ueber den Code. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
4ea7b37333 |
Anruf: "sie kriegt nur eine Benachrichtigung aber keinen Anruf"
Filipe, gerade gemeldet. Es waren ZWEI Fehler, und beide erklaeren
genau das, was er gesehen hat.
--- 1. Die Benachrichtigung sagte nicht, dass es ein Anruf ist ------
Gemessen kam bei ihr an:
Nachricht von [object Object]
(kein Text)
`nachricht.von` ist beim Klingeln ein OBJEKT (`{id, name}`) und keine
Zeichenkette; einen `text` gibt es bei einem Anruf gar nicht. Moeglich
wurde beides, weil die ART des Ereignisses die Benachrichtigung nie
erreichte: `chatEreignis` nimmt sie als vierten Parameter entgegen,
reichte sie aber nur in den Ereignisstrom weiter. Fuer den Push galt
jedes Ereignis als Chatnachricht -- auch das Klingeln.
Jetzt steht dort "Filipe ruft an" / "Tippen zum Rangehen", und beim
Tippen landet man im richtigen Gespraech.
--- 2. Und dort klingelte es dann trotzdem nicht --------------------
Das Klingeln lief ausschliesslich ueber den offenen Ereignisstrom. Wer
zusieht, hoert es. Wer die Seite NICHT offen hat, bekommt die
Benachrichtigung, tippt darauf, die Seite laedt -- und bleibt still.
Das Ereignis war vorbei, bevor sie da war.
Der Anruf funktionierte damit ausgerechnet fuer die nicht, fuer die
die Benachrichtigung ueberhaupt gebaut wurde.
Neu: `GET /workspace/api/anruf/offen` -- beim Laden fragt die Seite
einmal nach, ob in einem ihrer Raeume jemand wartet. Nur was noch
klingelt (45 s), nur Raeume, in denen die Person drin ist, und nicht
beim Anrufer selbst.
--- Gepruefte Wege -------------------------------------------------
pruef-anruf 80 -> 95. Zwei neue Abschnitte, beide mit Gegenprobe:
Route stillgelegt -> 3 rot; der Text ist jetzt einzeln pruefbar
(`pushTextFuer`), weil er vorher tief in einer Funktion entstand, die
nur der Push-Weg aufruft -- von aussen nicht messbar.
Nebenbei gefunden: `istDrinFuerAnruf` braucht die PERSON, nicht ihre
Nummer, und sagt das mit einem eigenen TypeError. Meine erste Fassung
uebergab die Nummer.
Co-Authored-By: Claude Opus 5 <[email protected]>
|
||
|
|
e933de3248 |
Anruf: der Lautsprecher -- und warum es kein Freisprech-Knopf ist
Filipe: "die funktion lautsprecher fehlt." Er ist das Gegenstueck zum Mikrofon: "Mikro aus" heisst, die anderen hoeren MICH nicht -- "Lautsprecher aus" heisst, ICH hoere sie nicht. Beides braucht man, aus verschiedenen Gruenden: das Mikro, wenn es bei einem selbst laut ist; den Lautsprecher, wenn nebenher etwas laeuft oder jemand ins Zimmer kommt. WAS BEWUSST NICHT GEBAUT WURDE: der Umschalter zwischen Hoermuschel und Freisprechen, den ein Telefon hat. Recherchiert statt geraten -- den gibt es im Browser nicht: Auf dem iPhone entscheidet Safari selbst, wohin der Ton geht, und Android laesst einzelne Toene gar nicht auf ein anderes Geraet legen (setSinkId ist dort nicht verfuegbar, weil die Plattform es nicht hergibt). Ein Knopf, der dort nichts tut, waere schlimmer als keiner -- man drueckt ihn und sucht den Fehler dann bei sich. Wo es GEHT (Rechner mit Chrome, Edge, Safari), steht dafuer eine echte Geraeteauswahl daneben. Sie erscheint nur, wenn es mehr als ein Geraet gibt UND die Namen bekannt sind; eine Auswahl mit einem Eintrag ist keine Auswahl, sondern eine Behauptung. DIE PRUEFUNG, AUF DIE ES ANKOMMT: Kommt jemand dazu oder geht jemand, werden die Toenelemente NEU GEBAUT -- und neue Elemente wissen nichts von einem Schalter, der vorher umgelegt wurde. Genau daran scheitern solche Knoepfe sonst: Sie wirken, bis sich die Runde aendert, und dann hoert man ploetzlich wieder mit. Gegenprobe (Anwenden nach dem Neuzeichnen ausgebaut) macht genau diese eine Pruefung rot, waehrend alle anderen gruen bleiben. Geprueft: pruef-anruf 73 -> 80, im echten Dreiergespraech im Browser. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
775c4b4207 |
Vermittlungsserver: coturn mit Zugangsdaten, die verfallen
Damit Anrufe auch in Netzen zustande kommen, die keine direkte Verbindung zulassen (15-25 % der Faelle). coturn ist installiert, steht aber still, bis die Konfiguration liegt -- ein coturn mit Werkseinstellung ist ein offenes Relais. KEIN FESTES PASSWORT. Es laege dauerhaft im Browser jedes Team-Mitglieds und liesse sich nie entziehen. Der Server rechnet stattdessen bei jeder Abfrage Zugangsdaten, die nach zwoelf Stunden verfallen (server/workspace-turn.js, coturns `use-auth-secret`). Das Geheimnis liegt in einer Datei, nicht in der Datenbank: einstellung- Setzen() schreibt Werte ins Protokoll, und coturn braucht denselben Wert ohnehin in /etc. Die Oberflaeche frischt die Daten vor jedem Anruf auf. Der Chat ist eine App, die tagelang offen bleibt -- wer nur beim Laden holt, telefoniert am zweiten Tag ohne Vermittlung, und es faellt nicht auf: Es scheitern nur die, die sie gebraucht haetten. DIE WICHTIGSTE ZEILE DER KONFIGURATION ist die Sperrliste. Gemessen: dreizehn Dienste lauschen auf diesem Server nur oertlich, darunter Caddys Verwaltung auf 127.0.0.1:2019 -- wer sie erreicht, kann jede Website umleiten. Ohne Sperrliste waere der Vermittlungsserver die Tuer dorthin, und die Anfrage saehe fuer Caddy aus wie von localhost. Geprueft: pruef-anruf.mjs 51 -> 73 Pruefungen. Gegenprobe (Geheimnis als Passwort ausliefern + fremde Zugangsdaten ueberschreiben) macht genau 5 rot, darunter "das Geheimnis steht NIRGENDS in der Antwort". tools/turn-probelauf.sh beweist am echten coturn, was ein fester Vergleichswert nicht kann: dass coturn unsere Rechnung akzeptiert. Beide Seiten koennten sonst konsequent falsch rechnen und jede Pruefung waere gruen. Seine eigene Gegenprobe war zweimal zu Recht rot -- der erste Aufbau mass wegen `-y` gar nicht das Ziel, das er zu messen behauptete, sondern coturns eingebauten Loopback-Schutz. Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
5bbf1f2019 |
Der Anruf funktioniert wirklich -- gefunden im Dreier-Test
GEBAUT WAR NICHT BEWIESEN. Der Anruf ist fuer bis zu vier Leute
geschrieben, geprueft war er zu zweit -- und der Zweier-Test pruefte
nur, dass der Kasten erscheint und die Uhr laeuft. Ob jemals TON
ankommt, hat er nie gefragt.
Der Dreier-Test hat es gefragt. Ergebnis beim ersten Lauf:
DogFather 0 Stroeme, "Verbunden"
rechte Hand 0 Stroeme, "Verbunden"
der Modi 0 Stroeme, "Verbindung ..."
DREI FEHLER, gefunden durch Messen statt Raten:
1. SIGNALE, DIE ZU FRUEH KOMMEN, WURDEN WEGGEWORFEN -- und das haette
JEDEN Anruf getroffen, auch zu zweit.
Wer rangeht, meldet "dabei". Der Anrufer bekommt das sofort und
schickt sein Angebot. Der Angerufene braucht danach aber noch ein
bis zwei Sekunden fuer das Mikrofon, und in dieser Zeit war `anruf`
noch null. Das Angebot wurde still verworfen; danach kommt keins
mehr. Beide Seiten zeigten "Verbindung ...", und es passierte nie
wieder etwas.
In der Spur war es unuebersehbar, sobald man hinsah:
ereignis:signal inhalt=angebot <- kommt an
ereignis:signal inhalt=weg
verbindung angelegt <- erst JETZT
Ein Signal ist keine Nachricht, die man wiederholen kann. Wer es
wegwirft, hat den Anruf verloren -- es wird jetzt aufgehoben und
abgearbeitet, sobald das Mikrofon steht.
2. DIE WARTESCHLANGE LAG ZUERST AN DER FALSCHEN STELLE. Eingebaut in
`signalVerarbeiten`, verworfen wurde aber schon eine Ebene hoeher.
Gefunden erst, als die Diagnose zeigte, dass `signalVerarbeiten` NIE
aufgerufen wird: keine einzige Konsolenzeile, obwohl beide Zweige
dort etwas melden. Eine Reparatur an der falschen Stelle sieht aus
wie eine Reparatur.
3. "VERBUNDEN" STAND DA, BEVOR ETWAS VERBUNDEN WAR. Die Meldung wurde
gesetzt, wenn jemand RANGEHT -- die Verbindung braucht danach noch
Sekunden. Den richtigen Zeitpunkt kennt nur die Verbindung selbst
(`onconnectionstatechange`). Eine Anzeige, die etwas Falsches sagt,
ist schlimmer als keine.
Dazu zwei Kleinigkeiten, die dabei auffielen: Das Abarbeiten laeuft
jetzt NACHEINANDER (ein Verbindungsweg, der vor der Beschreibung
ankommt, wird abgewiesen -- parallel gewinnt oft der Weg), und die
Warteschlange wird beim Auflegen geleert, damit kein altes Signal in
den naechsten Anruf wandert.
Und die Pruefung selbst hatte einen Fehler: Abschnitt 6 setzt erfundene
STUN-Adressen und liess sie stehen. Der Browser haette im naechsten
Abschnitt gegen deren Zeitlimit gekaempft statt gegen den Code. Sie
werden jetzt wieder geleert -- eine Pruefung, die der naechsten den
Boden verstellt, macht deren Ergebnis unlesbar.
pruef-anruf 40 -> 51. Der neue Abschnitt prueft das, worauf es
ankommt: Bei drei Teilnehmern muss JEDER zwei Stroeme haben. Haette
`verbindungen` eine einzelne Variable statt einer Karte, ginge es zu
zweit gut und der Dritte ueberschriebe stillschweigend den Ersten --
man sieht sich zu dritt und hoert einen.
Gegenprobe (Warteschlange wieder ausgebaut): 4 rot, genau die
Tonpruefungen.
Gruen: anruf, chat, css-klassen, namen.
Co-Authored-By: Claude Opus 5 <[email protected]>
|
||
|
|
93c9788782 |
Telefonieren im Chat -- und die Farben nach Nachbarschaft verteilt
ZWEI SACHEN IN EINEM COMMIT, weil beide aus derselben Nacht stammen. === 1. DIE FARBEN: DER RICHTIGE ABSTAND === Filipe: "es gibt noch mehr die aehnlich aussehen von den farben also leg los alle die sich aehnlich sind von den farben wechseln." Er hatte recht, und mein Denkfehler laesst sich benennen: Ich hatte den kleinsten Abstand ueber ALLE 37 Paare maximiert -- und der liegt bei 37 Farben zwangslaeufig bei 0,10. Das ist die Packungsgrenze, kein Versaeumnis. NUR SIEHT NIEMAND ALLE 37 NEBENEINANDER. Man sieht NACHBARN. Im Browser gemessen, an der tatsaechlichen Lage auf dem Schirm -- 125 Paare, die wirklich nebeneinander stehen, 15 davon unter 0,15: Creator-Profile / Zahlen 0,1019 beide rosa-rot LIVE-Analyse / Technik 0,1030 beide orange Wunschliste / Meldungen 0,1032 beide gelbgruen Wer sieht was / Entwicklung 0,1039 beide cyan Regeln & Hilfe / Mitmachen 0,1047 beide gruen Der Treff / Anschlagbrett 0,1070 beide rosa Die Farben bleiben, ihre ZUTEILUNG aendert sich: Nachbarabstand 0,1047 -> 0,2133 (mehr als verdoppelt) Nachbarn unter 0,15 6 -> 0 Die Nachbarschaft steht in server/kachel-nachbarn.json, gemessen im Browser -- nicht aus der Struktur im Quelltext abgeleitet. Die sagt, was zusammengehoert, nicht was zusammen zu sehen ist. === 2. TELEFONIEREN IM CHAT === Filipe: "kann man machen dass die modis, rechte hand und ich auch telefonieren koennen im chat?" ... "was man selbst in die app reinsetzten kann, nicht meinen pc belastet und trotzdem vielleicht in gruppe, mit video oder einzelnd." DER TON GEHT NICHT UEBER DEN SERVER. Direkt von Browser zu Browser (WebRTC); der Server reicht nur die Verbindungsdaten weiter, ein paar Kilobyte je Anruf. Zu zweit kodiert jedes Geraet einen Strom und dekodiert einen -- die Last eines gewoehnlichen Videoanrufs. KEIN NEUER DIENST. Der Chat hatte bereits alles: `chatEreignis()` fuer den Hinweg (SSE), Push fuers Klingeln, Raeume mit mehreren Teilnehmern. Ein eigener WebSocket daneben waere eine zweite Verbindung fuer dieselbe Frage -- und die zweite wird beim naechsten Umbau vergessen. Gebaut: Ton und Video, einzeln und in Gruppe bis GRUPPE_MAX (4), Klingeln mit Annehmen/Ablehnen, Mikro und Kamera schaltbar, Gespraechsdauer, Auflegen. Der Chat bleibt daneben benutzbar -- man schreibt oft, waehrend man spricht. EIN FUND, DER OHNE PRUEFLAUF LIVE GEGANGEN WAERE: Der Server sperrt Mikrofon und Kamera per Permissions-Policy auf ALLEN Seiten. Der erste Lauf meldete "microphone is not allowed in this document" -- der Anruf haette bei JEDEM versagt, mit einer Meldung, die auf die falsche Faehrte fuehrt (man sucht an den Browsereinstellungen). Die Sperre bleibt ueberall und ist an genau EINER Stelle geoeffnet: der Chat-Seite, und nur fuer sie selbst (`self`, nicht `*`). NOCH NICHT GEBAUT -- und ausdruecklich nicht heimlich: coturn. Ohne Vermittlungsserver klappen Anrufe nur im selben Netz. Die Adressen stehen in den Einstellungen statt im Quelltext; sie lassen sich nachtragen, ohne eine Zeile zu aendern. Ein oeffentlicher STUN-Dienst als Standard kam nicht in Frage: Er saehe bei jedem Anruf die IP-Adressen beider Teilnehmer, und fuer ein Team, das ueber Moderation und Vorfaelle spricht, ist das keine Kleinigkeit. server/pruef-anruf.mjs, 40 Pruefungen. Der wichtigste Abschnitt: Wer nicht in den Raum gehoert, kommt an KEINE Route. Ein Anruf hinterlaesst keine Spur -- wer mithoert, faellt nicht auf. Gegenproben, jede zielgenau: Empfaengerpruefung der Signalisierung weg -> 1 rot Raumpruefung weg -> 5 rot (jede Route offen) Kopfzeilen-Ausnahme weg -> 4 rot Gruen: anruf, chat, chat-optik, chat-kanaele, css-klassen, namen, struktur. Co-Authored-By: Claude Opus 5 <[email protected]> |