Die Chatkiste steht immer, Beenden raeumt auf -- und eine verspaetete Antwort ueberschreibt nichts Neueres mehr

Filipe, 30.09.2026: „wenn eine sendung beendet wurde soll alles
automatisch verschwinden. also von daten die da stehen von dem video."
Und: „mach dass ich die chat kiste immer sehe bitte und nicht erst
wenn das video läuft."

=====================================================================
1. BEENDET HEISST LEER
=====================================================================
Beim Beenden verschwinden Titel, YouTube-Adresse, Videotitel,
Vorschaubild, Beginn und das zweite Video samt seiner Stelle. Vorher
blieb alles stehen; beim naechsten Aufmachen stand dort der Titel der
letzten Sendung und ein Beginn, der in der Vergangenheit liegt.

ERST DER VERLAUF, DANN DAS LEEREN -- weg vom Schreibtisch heisst
nicht weg aus der Welt. Geprueft: Nach dem Beenden steht die Sendung
weiter in `reaktion_verlauf`, samt Video.

EINE FELDLISTE, NICHT ZWEI. Die Aufzaehlung stand in der
Zuruecksetzen-Route; jetzt brauchen sie zwei Wege. `SENDUNGS_FELDER`
und `PULT_EINSTELLUNGEN` stehen einmal oben. Zwei Abschriften waeren
die, die beim naechsten Spaltenzuwachs auseinanderlaufen -- genau so
sind am 11.09. drei Spalten mit Inhalt verlorengegangen.

Einstellungen (Bildaufteilung, Kameragroesse, Chatmodus) und die
Warteschlange bleiben -- die raeumt weiter nur „Alles zuruecksetzen"
ab. Wer nur vorbereitet und dann zumacht, behaelt seine Eingaben; das
ist eine Entscheidung und steht als Pruefung fest.

DAS FELD „BEGINN" WURDE NIE GELEERT, nur gefuellt:
`if (lage.beginnt_am && document.activeElement !== …)` -- zwei Fragen
in einer Bedingung, und die zweite beantwortete die erste
stillschweigend mit nein. Das betraf auch den vorhandenen
Zuruecksetzen-Knopf.

=====================================================================
2. DIE CHATKISTE STEHT IMMER
=====================================================================
Sie lag in `#teil-live` und ging mit ihm weg. Im Wartebereich stand
dabei woertlich „Der Chat ist schon offen" -- der Satz stimmte sogar,
der Server laesst dort schreiben (201, geprueft); zu sehen war der
Chat trotzdem nicht.

Die drei Haeute und die Schiene liegen jetzt in einem gemeinsamen
`.raum`: links wechseln die Haeute, rechts steht der Chat. Das Raster
wandert mit nach oben statt sich zu verdoppeln -- zwei
`grid-template-columns` fuer dieselbe Frage waren am 28.09. der
Grund, warum die Regieleiste null Pixel bekam.

Wer nicht schreiben darf, liest den Grund im Feld („Der Saal ist zu").

DER UMBAU HAT DREI DINGE VERSCHOBEN, und die Messung hat sie sofort
gefunden: Kino 0 px hoch, Spendenkarte bei x=1458 ausserhalb des
Bildes, „Kamera aus"-Schild kam nicht an. Eine Ursache: Zwei Regeln
(`grid-row: 2` und `grid-area: kino`) zielten auf die drei Haeute,
weil die frueher unmittelbar im Saal lagen. Gefunden hat das nicht
das Nachdenken, sondern eine Messung der ganzen Kette --
`teil-live 0x0 @1440,742` lag NEBEN dem Raum.

=====================================================================
3. „DER SAAL IST ZU" ERREICHTE NIEMANDEN, DER DARIN SASS
=====================================================================
`stand: "zu"` loescht `reaktion_dabei`, und `melden()` suchte seine
Empfaenger erst danach -- in genau dieser geleerten Liste. Die Seiten
der Zuschauer blieben auf „live" stehen.

`melden(art, daten, wer)` nimmt jetzt eine Empfaengerliste; die
Stand-Route bestimmt sie VOR jeder Aenderung.

UND EINE ZUGESPERRTE SEITE VERSTUMMTE FUER IMMER. War der Saal zu,
hielt `anschliessen()` jeden Takt an -- und Rundrufe bekam sie auch
keine, weil sie in keiner Liste mehr stand. Machte der Host wieder
auf, passierte bei allen mit offenem Reiter NICHTS. Nicht in dreissig
Sekunden: nie, bis jemand von Hand neu lud. Gemessen: Server sagt
„vorbereitung", die Seite zeigt „zu", auch nach 40 Sekunden.
Jetzt fragt eine geschlossene Seite alle 15 Sekunden nach -- der
ruhigste Zustand des Hauses, dort kostet das nichts.

=====================================================================
4. WER ZULETZT ANTWORTET, HAT NICHT RECHT
=====================================================================
Eine Abfrage ist Sekundenbruchteile unterwegs. Kommt in dieser Zeit
ein Rundruf an, ist SEIN Stand der neuere -- und trotzdem hat die
verspaetete Antwort ihn ueberschrieben.

Gemessen: Die Regie schaltet die Werbung auf Wechsel, der Rundruf
zeichnet ihn, und einen Augenblick spaeter steht wieder Laufband.
Eine feste Wartezeit in der Messung hatte das zugedeckt; es fiel
erst auf, als ich sie durch eine echte Bedingung ersetzte.

Drei Runden Raten lagen daneben. Gefunden hat es eine Mitschrift der
Aufrufkette:

    wechsel:2  @ EventSource
    laufband:2 @ zeichnen < standHolen < signalEmpfangen

Eine Folgenummer zaehlt jeden angekommenen Rundruf. Jede Abfrage, die
die Seite VON SICH AUS macht -- nach dem Verbindungsaufbau, im
geschlossenen Saal, bei jedem Verbindungssignal, nach einer
abgelehnten Anwesenheitsmeldung, und der Anwesenheitstakt selbst --
verwirft ihr Ergebnis, wenn inzwischen etwas Neueres da war.

Abfragen nach einer EIGENEN Handlung bleiben hart: Sie bringen Dinge
mit, die kein Rundruf traegt (die Verwaltungslisten der Regie). Die
Unterscheidung steht am Aufruf, nicht in einer Bedingung im Inneren.

=====================================================================
5. UND DIE MESSUNGEN WARTEN NICHT MEHR AUF DIE UHR
=====================================================================
Feste Wartezeiten sind dieselbe Falle wie feste Schwellen: Sie
stimmen, bis daneben etwas langsamer wird, und melden dann einen
Fehler, den es nicht gibt. Zweimal ist mir das in einer Stunde
passiert -- einmal beim Band, einmal bei den Ecken. Der Werbeblock
wartet jetzt fuenfmal auf das Ergebnis, der Gleichlauf viermal; laeuft
eine Frist ab, ist es ein echter Befund.

Die erste Fassung der Gleichlauf-Gegenprobe war selbst falsch -- sie
hielt das Selbstheilen des Systems fuer einen Fehler.

Neu in mess-reaktion: „Die Chatkiste in jedem Zustand" (alle drei,
mit Breite und Sperrgrund) und „Beendet heisst leer (im Pult)" -- dort
gemessen, wo Filipe hinsieht, nicht in der Datenbank.
Neu in pruef-reaktion (+14): das Leeren, die Gegenprobe „vorher war
etwas drin", der Verlauf bleibt, und die Entscheidung „wer nur
vorbereitet hat, behaelt seine Eingaben".

Gemessen: mess-reaktion dreimal hintereinander 0/0, pruef-reaktion
407/0, pruef-css-klassen 37/0, pruef-struktur 44/0, pruef-tippziele
13/0, pruef-haus-seiten 38/0, pruef-haus-trennung 100/0, pruef-buehne
38/0, pruef-push-ziel 38/0, mess-buehne 0.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
2026-09-30 02:36:13 +02:00
co-authored by Claude Opus 5
parent 2c3b90d75c
commit 0fbd3f45b3
50 changed files with 1644 additions and 933 deletions
+99
View File
@@ -2475,6 +2475,14 @@ melde("=== Die Einladung ===");
live, und eingeladen wird nur beim Uebergang. */
await roh(CREW, "/workspace/api/reaktion/stand",
{ method: "POST", keks: k.admin, rumpf: { stand: "zu" } });
/* UND DANN EIN NEUES VIDEO. Seit dem 30.09. raeumt das Beenden die
Sendungsdaten weg -- ohne Video gibt es kein „auf Sendung" (400
kein_video). Genau das tut ein Mensch auch: Nach der einen
Sendung traegt er fuer die naechste ein neues Video ein. */
await roh(CREW, "/workspace/api/reaktion", {
method: "PUT", keks: k.admin,
rumpf: { titel: "Die naechste Sendung", video: "dQw4w9WgXcQ" },
});
const db2 = new DatabaseSync(process.env.WORKSPACE_DB);
/* Je ein Geraet fuer JEDEN -- auch fuer die, die keine Einladung
@@ -2559,6 +2567,97 @@ melde("=== Die Einladung ===");
await new Promise((r) => dienst.close(r));
}
/* =======================================================================
BEENDET HEISST LEER -- aber nicht vergessen
=======================================================================
Filipe, 30.09.2026, mit einem Bild des Pults („Titel der Sendung:
Test / YouTube: bm0sK2-KkW8 / Beginn: 29.09.2026 18:36"):
„wenn eine sendung beendet wurde soll alles automatisch
verschwinden. also von daten die da stehen von dem video."
Vorher blieb alles stehen. Beim naechsten Aufmachen stand dort der
Titel der letzten Sendung und ein Beginn, der in der Vergangenheit
liegt.
======================================================================= */
melde("");
melde("=== Beendet heisst leer ===");
{
/* Die Sendung laeuft an dieser Stelle noch (aus dem Abschnitt
darueber). Erst werden die Felder gefuellt, damit es ueberhaupt
etwas zu leeren gibt. */
const beginn = new Date(Date.now() + 3600_000).toISOString();
await roh(CREW, "/workspace/api/reaktion", {
method: "PUT", keks: k.admin,
rumpf: { titel: "Die letzte Sendung", video: "dQw4w9WgXcQ",
vorschau: "https://example.invalid/bild.jpg", beginnt_am: beginn },
});
await roh(CREW, "/workspace/api/reaktion/zweitvideo", {
method: "POST", keks: k.admin, rumpf: { video: "bm0sK2-KkW8" },
});
/* DIE VORAUSSETZUNG WIRD HERGESTELLT UND GEPRUEFT.
Ohne das misst dieser Abschnitt bei der kleinsten Aenderung
weiter oben etwas ganz anderes -- und meldet es als Befund. */
await roh(CREW, "/workspace/api/reaktion/stand",
{ method: "POST", keks: k.admin, rumpf: { stand: "live" } });
/* ---- DIE GEGENPROBE STEHT VORNE ----------------------------
Ohne sie bestuende die Pruefung darunter auch dann, wenn nie
etwas in den Feldern stand. */
const vorher = await roh(CREW, "/workspace/api/reaktion", { keks: k.admin });
ok(vorher.j?.titel === "Die letzte Sendung" && !!vorher.j?.video,
`vorher stehen Titel und Video im Pult ("${vorher.j?.titel}")`);
ok(!!vorher.j?.beginnt_am, "und ein Beginn");
ok(!!vorher.j?.zweit?.video, `und ein zweites Video (${vorher.j?.zweit?.video})`);
ok(vorher.j?.stand === "live",
`und die Sendung laeuft wirklich -- sonst misst dieser Abschnitt `
+ `etwas anderes (${vorher.j?.stand})`);
const anAus = await roh(CREW, "/workspace/api/reaktion/stand",
{ method: "POST", keks: k.admin, rumpf: { stand: "zu" } });
ok(anAus.code === 200, `die Sendung wird beendet (${anAus.code})`);
const nachher = await roh(CREW, "/workspace/api/reaktion", { keks: k.admin });
ok(!nachher.j?.titel, `der Titel ist weg ("${nachher.j?.titel}")`);
ok(!nachher.j?.video, `das Video ist weg ("${nachher.j?.video}")`);
ok(!nachher.j?.vorschau, "das Vorschaubild ist weg");
ok(!nachher.j?.beginnt_am, `der Beginn ist weg (${nachher.j?.beginnt_am})`);
ok(!nachher.j?.zweit?.video, "das zweite Video ist weg");
ok(!nachher.j?.laeuft && !nachher.j?.sekunde, "und die Stelle steht auf null");
/* ---- WEG VOM SCHREIBTISCH, NICHT AUS DER WELT ---------------
Der Verlaufseintrag wird VOR dem Leeren geschrieben. Schoebe
jemand das Leeren nach vorn, stuende dort ein leerer Titel --
und es faellt erst auf, wenn jemand nachsehen will, was letzte
Woche lief. */
const dbv = new DatabaseSync(process.env.WORKSPACE_DB);
const letzte = dbv.prepare(
"SELECT titel, video FROM reaktion_verlauf ORDER BY id DESC LIMIT 1").get();
dbv.close();
ok(letzte?.titel === "Die letzte Sendung",
`im Verlauf steht sie weiter ("${letzte?.titel}")`);
ok(letzte?.video === "dQw4w9WgXcQ", `samt Video (${letzte?.video})`);
/* ---- WER NUR VORBEREITET HAT, BEHAELT SEINE EINGABEN --------
Eine Entscheidung, und deshalb steht sie hier: Aus der
Vorbereitung zumachen heisst „ich hoere auf vorzubereiten",
nicht „ich habe gesendet". Ohne diese Zeile nimmt eine spaetere,
gut gemeinte Vereinfachung („leeren wir doch bei jedem zu") die
Eingaben unbemerkt weg. */
await roh(CREW, "/workspace/api/reaktion", {
method: "PUT", keks: k.admin,
rumpf: { titel: "Nur vorbereitet", video: "dQw4w9WgXcQ" },
});
await roh(CREW, "/workspace/api/reaktion/stand",
{ method: "POST", keks: k.admin, rumpf: { stand: "vorbereitung" } });
await roh(CREW, "/workspace/api/reaktion/stand",
{ method: "POST", keks: k.admin, rumpf: { stand: "zu" } });
const probe = await roh(CREW, "/workspace/api/reaktion", { keks: k.admin });
ok(probe.j?.titel === "Nur vorbereitet",
`wer nur vorbereitet hat, behaelt seine Eingaben ("${probe.j?.titel}")`);
}
melde("");
melde(`${geprueft} Pruefungen, ${fehler} Fehler`);
melde(fehler === 0 ? "ALLES IN ORDNUNG" : "NICHT IN ORDNUNG");