Nachtrag zu B4: die Aufraeumung lief nie -- .changes an der falschen Stelle

WAS PASSIERT IST

Der Commit 60802170 ist um 23:33 ausgeliefert worden. Im Protokoll des
Servers stand danach:

    [workspace] undefined zurueckgenommene Nachrichten endgueltig entfernt.
    [workspace] 4 blaue Herzen auf babyblau umgestellt

Die zweite Zeile stimmt. Die erste ist der Befund: `undefined` statt
einer Zahl. Nachgesehen in der Live-Datenbank -- die fuenf
zurueckgenommenen Zeilen waren noch da.

URSACHE: `d.prepare("DELETE ...").changes` statt `.run().changes` --
also die Eigenschaft der vorbereiteten ANWEISUNG, nicht die des Laufs.
Die Anweisung wurde vorbereitet und nie ausgefuehrt. Nichts stuerzte
ab, nichts war rot, kein Fehler im Protokoll.

NEBENFOLGE, die es schlimmer gemacht haette: Die Sicherung wird VOR
dem Loeschen geschrieben. Da nie geloescht wurde, blieb die Bedingung
erfuellt -- bei JEDEM Neustart waere eine weitere Sicherungsdatei
entstanden. Genau das, wovor der Kommentar zwei Zeilen darueber warnt.
Ein Kommentar, der vor einem Fehler warnt, verhindert ihn nicht.

WAS JETZT ANDERS IST

Behoben, und die Umstellung ist eine exportierte Funktion geworden:
`loeschspurenAufraeumen(d, dbPfad)`. Nicht aus Ordnungsliebe -- eine
Umstellung, die tief im Hochfahren steckt, kann keine Pruefung
aufrufen. Eine Funktion schon.

pruef-loeschen.mjs (20 -> 25): stellt den alten Zustand her (Text leer,
weg_am gesetzt), ruft die Funktion und misst
  * dass sie eine ZAHL meldet (`typeof === "number"`) -- genau hier
    stand undefined, und 0 sieht in einer Meldung fast so aus,
  * dass die Zeile wirklich verschwindet,
  * und dass beim zweiten Aufruf nichts mehr passiert und nichts
    gesichert wird.

Ohne den ersten dieser drei Haken waere der Fehler auch beim naechsten
Mal durchgegangen: Eine Meldung mit `undefined` ist gruen, solange
niemand sie liest.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
2026-09-22 23:36:02 +02:00
co-authored by Claude Opus 5
parent 60802170a1
commit 73facb7ce9
2 changed files with 134 additions and 55 deletions
+80 -55
View File
@@ -2191,6 +2191,85 @@ export function kanaeleFuer(person) {
? KANAELE : [];
}
/* =====================================================================
DIE ALTEN ZURUECKGENOMMENEN NACHRICHTEN GEHEN GANZ (22.09.2026)
=====================================================================
Filipe: "Geloeschte Nachrichten verschwinden vollstaendig -- keine
Spur, kein ‚wurde geloescht'-Hinweis, bei niemandem, auch nicht bei
DogFather."
Seit dem 22.09.2026 loescht der Chat wirklich (DELETE). Was davor
zurueckgenommen wurde, steht aber noch als Zeile da: Text leer,
`weg_am` gesetzt. Die Anzeige zeigt sie nicht mehr -- in der
Datenbank waere sie trotzdem. "Keine Spur" gaelte dann fuer alles,
was ab heute passiert, und nicht fuer das, was ihn zu dem Satz
gebracht hat.
ES GEHT NUR UM ZEILEN OHNE INHALT. `text = ''` ist die Bedingung,
nicht bloss `weg_am IS NOT NULL`: Der Text wurde beim Zuruecknehmen
geleert, also ist hier nichts zu verlieren. Stuende irgendwo doch
noch Text in einer zurueckgenommenen Zeile, bliebe sie liegen --
lieber ein Rest, den jemand ansehen kann, als ein stiller Verlust.
WIEDERHOLBAR: Beim zweiten Start gibt es nichts mehr zu tun, und es
passiert nichts. GESICHERT WIRD VORHER -- aber nur, wenn es wirklich
etwas zu tun gibt. Eine Sicherung bei jedem Start waere eine Datei
je Neustart und damit kein Schutz, sondern Muell.
---------------------------------------------------------------------
ALS EIGENE, EXPORTIERTE FUNKTION -- und das hat einen Grund, der an
genau dieser Stelle entstanden ist.
In der ersten Fassung stand `d.prepare("DELETE ...").changes` statt
`.run().changes`: die Eigenschaft der vorbereiteten ANWEISUNG, nicht
die des Laufs. Die Anweisung wurde vorbereitet und nie ausgefuehrt.
Nichts stuerzte ab, nichts war rot. Im Protokoll stand "undefined
zurueckgenommene Nachrichten endgueltig entfernt" -- ein Zaehler,
der `undefined` sagt, hat nicht gezaehlt, und was nicht zaehlt, hat
meist auch nicht gearbeitet. Die fuenf Zeilen blieben liegen, und
weil die Sicherung VOR dem Loeschen geschrieben wird, waere bei
jedem Neustart eine weitere Sicherungsdatei entstanden.
Eine Umstellung, die tief im Hochfahren steckt, kann keine Pruefung
aufrufen. Eine Funktion schon: pruef-loeschen.mjs gibt ihr eine
vorbereitete Datenbank und sieht nach, was sie WIRKLICH getan hat.
@returns {{entfernt:number, gesichert:string|null}}
===================================================================== */
export function loeschspurenAufraeumen(d, dbPfad = DB_PFAD) {
try {
const spalten = d.prepare("PRAGMA table_info(chat_nachrichten)").all().map((x) => x.name);
if (!spalten.includes("weg_am")) return { entfernt: 0, gesichert: null };
const offen = d.prepare(
"SELECT COUNT(*) AS n FROM chat_nachrichten WHERE weg_am IS NOT NULL AND text = ''").get().n;
if (offen === 0) return { entfernt: 0, gesichert: null };
let sicherung = null;
if (dbPfad) {
const stempel = new Date().toISOString().replace(/[-:T]/g, "").slice(0, 14);
sicherung = `${dbPfad}.vor-loeschspuren-${stempel}`;
d.exec(`VACUUM INTO '${sicherung.replace(/'/g, "''")}'`);
console.log("[workspace] Sicherung vor dem Aufraeumen:", sicherung);
}
/* Reaktionen und Erwaehnungen haengen mit ON DELETE CASCADE daran
und gehen von selbst mit -- `PRAGMA foreign_keys = ON` steht
beim Oeffnen. */
const entfernt = d.prepare(
"DELETE FROM chat_nachrichten WHERE weg_am IS NOT NULL AND text = ''").run().changes;
/* Und die Raeume rechnen ihren letzten Zeitpunkt neu -- sonst
stuende einer oben in der Liste, zu dessen Zeitpunkt es nichts
mehr gibt. */
d.exec(`UPDATE chat_raeume SET letzte_am =
(SELECT MAX(erstellt) FROM chat_nachrichten WHERE raum_id = chat_raeume.id)`);
console.log(`[workspace] ${entfernt} zurueckgenommene Nachrichten endgueltig entfernt.`);
return { entfernt, gesichert: sicherung };
} catch (fehler) {
console.error("[workspace] Aufraeumen der Loeschspuren:", fehler?.message);
return { entfernt: 0, gesichert: null };
}
}
export function kategorienFuer(person) {
if (!person) return [];
return TEAM_DOGI_ROLLEN.has(person.rolle) || person.rolle === "admin"
@@ -3459,61 +3538,7 @@ function umstellungen(d) {
}
}
/* =====================================================================
DIE ALTEN ZURUECKGENOMMENEN NACHRICHTEN GEHEN GANZ (22.09.2026)
=====================================================================
Filipe: „Geloeschte Nachrichten verschwinden vollstaendig -- keine
Spur, kein ‚wurde geloescht'-Hinweis, bei niemandem, auch nicht
bei DogFather."
Seit heute loescht der Chat wirklich (DELETE). Was davor
zurueckgenommen wurde, steht aber noch als Zeile da: Text leer,
`weg_am` gesetzt. Die Anzeige zeigt sie nicht mehr -- in der
Datenbank waere sie trotzdem. „Keine Spur" gilt dann fuer alles,
was ab heute passiert, und nicht fuer das, was ihn zu dem Satz
gebracht hat.
ES GEHT NUR UM ZEILEN OHNE INHALT. `text = ''` ist die Bedingung,
nicht bloss `weg_am IS NOT NULL`: Der Text wurde beim Zuruecknehmen
geleert, also ist hier nichts zu verlieren. Stuende irgendwo doch
noch Text in einer zurueckgenommenen Zeile, bliebe sie liegen --
lieber ein Rest, den jemand ansehen kann, als ein stiller Verlust.
WIEDERHOLBAR: Beim zweiten Start gibt es nichts mehr zu tun, und
es passiert nichts. Deshalb steht es hier bei den Umstellungen
und nicht in einem einmaligen Skript, das jemand vergessen
koennte.
GESICHERT WIRD VORHER -- aber nur, wenn es wirklich etwas zu tun
gibt. Eine Sicherung bei jedem Start waere eine Datei je Neustart
und damit kein Schutz, sondern Muell. */
try {
const spalten = d.prepare("PRAGMA table_info(chat_nachrichten)").all().map((x) => x.name);
if (spalten.includes("weg_am")) {
const offen = d.prepare(
"SELECT COUNT(*) AS n FROM chat_nachrichten WHERE weg_am IS NOT NULL AND text = ''").get().n;
if (offen > 0) {
const stempel = new Date().toISOString().replace(/[-:T]/g, "").slice(0, 14);
const sicherung = `${DB_PFAD}.vor-loeschspuren-${stempel}`;
d.exec(`VACUUM INTO '${sicherung.replace(/'/g, "''")}'`);
console.log("[workspace] Sicherung vor dem Aufraeumen:", sicherung);
/* Reaktionen und Erwaehnungen haengen mit ON DELETE CASCADE
daran und gehen von selbst mit -- `PRAGMA foreign_keys = ON`
steht beim Oeffnen. Nachgesehen, nicht angenommen. */
const weg = d.prepare(
"DELETE FROM chat_nachrichten WHERE weg_am IS NOT NULL AND text = ''").changes;
/* Und die Raeume rechnen ihren letzten Zeitpunkt neu -- sonst
stuende einer oben in der Liste, zu dessen Zeitpunkt es
nichts mehr gibt. */
d.exec(`UPDATE chat_raeume SET letzte_am =
(SELECT MAX(erstellt) FROM chat_nachrichten WHERE raum_id = chat_raeume.id)`);
console.log(`[workspace] ${weg} zurueckgenommene Nachrichten endgueltig entfernt.`);
}
}
} catch (fehler) {
console.error("[workspace] Aufraeumen der Loeschspuren:", fehler?.message);
}
loeschspurenAufraeumen(d);
/* =====================================================================
DIE BLAUEN HERZEN WERDEN BABYBLAU (22.09.2026)