der Notiz
Jede Nacht laeuft tools/nachtlauf.mjs ueber 193 Pruefdateien und
schreibt das Ergebnis als Notiz in Filipes Vault. Diese Notiz ist das
Einzige, was er von dem ganzen Aufwand zu sehen bekommt.
Und sie war die einzige Datei im Haus, die niemand geprueft hat.
=== WARUM DAS VORHER GAR NICHT GING ===
nachtlauf.mjs startet BEIM IMPORT sofort einen Lauf. Wer die Notiz
messen wollte, haette damit eine halbe Stunde Rechenzeit ausgeloest --
und nebenbei das echte Ergebnis auf Filipes Startseite ueberschrieben.
Am 04.09.2026 hat genau so ein Griff bei RunOne einen roten Alarm ueber
einer fehlerfreien Buchhaltung erzeugt, am Morgen einer Vorstellung.
„Ein Prueflauf ist kein harmloser Lesevorgang."
Der Textbau steht deshalb jetzt in tools/nachtlauf-notiz.mjs: nur
Textbau, kein Nebeneffekt, Importieren ist folgenlos.
=== UND EIN STUECK DER NOTIZ GEHOERT AB JETZT DEM MENSCHEN ===
Oben in der Notiz stand: „von Hand aendern bringt nichts, beim
naechsten Lauf ist es wieder ueberschrieben." Das war ehrlich und
trotzdem falsch herum gedacht.
Das Wertvollste an diesem Pruefstand ist naemlich nicht die Zahl,
sondern der Satz daneben: „die hier ist rot, weil sie den Umbau vom
24.09. nicht mitbekommen hat" oder „die ist echt, Finger weg". Genau
dieser Satz wurde jede Nacht geloescht. Heute Nacht standen 37
Dateien in der Liste, und bei 25 davon waere so ein Satz die halbe
Arbeit gewesen.
Jetzt gibt es einen Bereich zwischen zwei Marken, den der Lauf wieder
einsetzt statt ihn zu ueberschreiben. Fehlt er, wird er leer neu
angelegt -- mit der Erklaerung darin, wozu er da ist.
=== 30 PRUEFUNGEN, JEDE MIT GEGENPROBE ===
1. Der Block „von Hand" ueberlebt einen Lauf -- UND das alte
Ergebnis daneben verschwindet wirklich. Ohne die zweite Haelfte
wuerde die erste nur beweisen, dass die Datei nicht angefasst
wurde.
2. Ein leerer Block zaehlt als nicht vorhanden; sonst waere die
Erklaerung fuer immer weg, sobald jemand sie einmal loescht.
3. Weniger Pruefungen als gestern werden gemeldet, auch wenn alles
gruen ist (die Lehre vom 28.08.: 789 statt 804, gruen, und acht
Pruefungen je Geraet waren still weggefallen). Gegenprobe: Bei
MEHR Pruefungen darf der Satz NICHT kommen -- eine Warnung, die
immer kommt, ist keine Warnung mehr.
4. Der dritte Ausgang sagt „konnte nicht nachsehen", nennt den Grund
und wie alt der letzte echte Stand ist -- und behauptet nirgends,
alles sei in Ordnung.
5. „Keine Pruefung gezaehlt" wird als das benannt, was es ist: kein
Befund, sondern ein Zaehlproblem. Acht Dateien standen heute Nacht
aus diesem Grund als rot da.
Punkt 5 der Pruefung hat sich beim ersten Lauf gleich bezahlt gemacht:
Sie zaehlt nach, ob JEDER der vier Aufrufe in nachtlauf.mjs den
Notizpfad mitreicht. Drei davon sind die seltenen Ausgaenge, die man
beim Ausprobieren nie erwischt -- und an jedem einzelnen waere der
Block „von Hand" spurlos verschwunden. Beim Umbau hatte ich genau
diese drei zuerst vergessen.
GEMESSEN: 30 Pruefungen, 0 Fehler. Die echte Notiz im Vault wird dabei
nicht angefasst -- gearbeitet wird in einem mktemp-Ordner.
Co-Authored-By: Claude Opus 5 <[email protected]>
167 lines
7.4 KiB
JavaScript
167 lines
7.4 KiB
JavaScript
/* =====================================================================
|
||
DIE NOTIZ FÜR DEN PRÜFSTAND (ausgelagert am 25.09.2026)
|
||
|
||
Stand vorher mitten in `nachtlauf.mjs`. Ausgelagert, damit sie
|
||
PRÜFBAR wird: `nachtlauf.mjs` startet beim Import sofort einen
|
||
Lauf über alle Prüfdateien -- wer die Notiz messen wollte, hätte
|
||
dafür eine halbe Stunde Rechenzeit ausgelöst und nebenbei das
|
||
echte Ergebnis auf Filipes Startseite überschrieben.
|
||
|
||
Hier drin steht nur Textbau, kein Nebeneffekt. Importieren ist
|
||
folgenlos. Geprüft von `server/pruef-nachtlauf.mjs`.
|
||
===================================================================== */
|
||
import { readFileSync } from "node:fs";
|
||
|
||
const zweiZiffern = (n) => String(n).padStart(2, "0");
|
||
function zeitWort(d = new Date()) {
|
||
return `${zweiZiffern(d.getDate())}.${zweiZiffern(d.getMonth() + 1)}.${d.getFullYear()}`
|
||
+ ` um ${zweiZiffern(d.getHours())}:${zweiZiffern(d.getMinutes())}`;
|
||
}
|
||
|
||
export { zeitWort };
|
||
|
||
/* =====================================================================
|
||
EIN STÜCK DER NOTIZ GEHÖRT DEM MENSCHEN (25.09.2026)
|
||
|
||
Bis heute stand oben in der Notiz: "von Hand ändern bringt nichts,
|
||
beim nächsten Lauf ist es wieder überschrieben." Das war ehrlich
|
||
und trotzdem falsch herum gedacht.
|
||
|
||
Denn das Wertvollste an diesem Prüfstand ist gerade NICHT die
|
||
Zahl, sondern der Satz daneben: "Diese Prüfung ist rot, weil sie
|
||
den Umbau vom 24.09. nicht mitbekommen hat" oder "die hier ist
|
||
echt, Finger weg". Wer das aufschreiben will, schreibt es genau
|
||
hierhin -- und fand es am nächsten Morgen gelöscht.
|
||
|
||
Deshalb gibt es jetzt einen Bereich zwischen zwei Marken, den der
|
||
Lauf beim Schreiben WIEDER EINSETZT statt ihn zu überschreiben.
|
||
Fehlt er (neue Notiz, jemand hat ihn gelöscht), wird er leer neu
|
||
angelegt -- mit der Erklärung drin, wozu er da ist.
|
||
===================================================================== */
|
||
export const MARKE_AUF = "<!-- VON HAND — dieser Teil bleibt stehen -->";
|
||
export const MARKE_ZU = "<!-- ENDE VON HAND -->";
|
||
|
||
export function handNotizLesen(NOTIZ) {
|
||
let alt = "";
|
||
try { alt = readFileSync(NOTIZ, "utf8"); } catch { return null; }
|
||
const a = alt.indexOf(MARKE_AUF);
|
||
const b = alt.indexOf(MARKE_ZU, a + 1);
|
||
if (a < 0 || b < 0) return null;
|
||
const drin = alt.slice(a + MARKE_AUF.length, b).trim();
|
||
return drin.length ? drin : null;
|
||
}
|
||
|
||
export function notizSchreiben({ lage, vorher, minuten, konnteNicht }, notizPfad) {
|
||
const jetzt = new Date();
|
||
const hand = handNotizLesen(notizPfad);
|
||
const zeilen = [];
|
||
zeilen.push("# Prüfstand Team Dogi");
|
||
zeilen.push("");
|
||
zeilen.push("*Diese Notiz schreibt `tools/nachtlauf.mjs`. Alles außerhalb des Blocks");
|
||
zeilen.push("„VON HAND“ ist beim nächsten Lauf wieder überschrieben — was DRIN steht,");
|
||
zeilen.push("bleibt stehen.*");
|
||
zeilen.push("");
|
||
zeilen.push(MARKE_AUF);
|
||
if (hand) {
|
||
zeilen.push(hand);
|
||
} else {
|
||
zeilen.push("## Von Hand");
|
||
zeilen.push("");
|
||
zeilen.push("*Hier hinein gehört, was die Zahlen nicht sagen können: welche rote");
|
||
zeilen.push("Zeile schon untersucht ist, welche echt ist, woran gerade gearbeitet");
|
||
zeilen.push("wird. Dieser Block überlebt jeden Lauf — der Rest der Notiz nicht.*");
|
||
}
|
||
zeilen.push(MARKE_ZU);
|
||
zeilen.push("");
|
||
|
||
if (konnteNicht) {
|
||
zeilen.push("## ⚠ Konnte nicht nachsehen");
|
||
zeilen.push("");
|
||
zeilen.push(konnteNicht);
|
||
zeilen.push("");
|
||
if (vorher?.wann) {
|
||
const tage = Math.floor((Date.now() - vorher.zeit) / 86400000);
|
||
zeilen.push(`Zuletzt wirklich gemessen: **${vorher.wann}**`
|
||
+ (tage >= 1 ? ` — das ist **${tage} Tag${tage === 1 ? "" : "e"} her**.` : "."));
|
||
} else {
|
||
zeilen.push("Es gibt noch kein früheres Ergebnis.");
|
||
}
|
||
return zeilen.join("\n") + "\n";
|
||
}
|
||
|
||
const rot = lage.rote.length;
|
||
zeilen.push(rot
|
||
? `## ${rot} Prüfung${rot === 1 ? "" : "en"} ${rot === 1 ? "ist" : "sind"} rot`
|
||
: "## Alles in Ordnung");
|
||
zeilen.push("");
|
||
zeilen.push(`Gemessen am **${zeitWort(jetzt)}**, Dauer ${minuten} Minuten.`);
|
||
zeilen.push("");
|
||
zeilen.push("| | jetzt | vorher |");
|
||
zeilen.push("|---|---|---|");
|
||
zeilen.push(`| Dateien in Ordnung | ${lage.dateienOk} von ${lage.dateien} | `
|
||
+ `${vorher ? `${vorher.dateienOk} von ${vorher.dateien}` : "—"} |`);
|
||
zeilen.push(`| Einzelprüfungen bestanden | ${lage.punkteOk} von ${lage.punkte} | `
|
||
+ `${vorher ? `${vorher.punkteOk} von ${vorher.punkte}` : "—"} |`);
|
||
zeilen.push("");
|
||
|
||
/* EIN RÜCKGANG DER ANZAHL IST EIN BEFUND, AUCH WENN ALLES GRÜN IST.
|
||
Am 28.08.2026 meldete ein Lauf 789 statt 804 Prüfungen und war
|
||
dabei grün: Acht Prüfungen je Gerät waren stillschweigend
|
||
weggefallen -- ausgerechnet die, die die wichtigste Funktion
|
||
absichern. Wer nur "bestanden" liest, hätte sie ab da dauerhaft
|
||
ungeprüft gelassen. */
|
||
if (vorher && lage.punkte != null && vorher.punkte != null
|
||
&& lage.punkte < vorher.punkte) {
|
||
zeilen.push(`> **Es werden ${vorher.punkte - lage.punkte} Prüfungen WENIGER gemacht als beim letzten Lauf.**`);
|
||
zeilen.push("> Das ist auch dann ein Befund, wenn alles grün ist: Eine Prüfung, die");
|
||
zeilen.push("> still nicht mehr läuft, beweist nichts — und niemandem fällt es auf.");
|
||
zeilen.push("");
|
||
}
|
||
if (vorher && lage.dateien != null && vorher.dateien != null
|
||
&& lage.dateien < vorher.dateien) {
|
||
zeilen.push(`> **Es gibt ${vorher.dateien - lage.dateien} Prüfdatei(en) weniger als beim letzten Lauf.**`);
|
||
zeilen.push("");
|
||
}
|
||
|
||
if (rot) {
|
||
zeilen.push("## Was rot ist");
|
||
zeilen.push("");
|
||
for (const r of lage.rote) {
|
||
zeilen.push(`### \`${r.datei}\``);
|
||
zeilen.push("");
|
||
if (r.nullPunkte) {
|
||
zeilen.push(`**Kein Befund – es wurde keine einzige Prüfung GEZÄHLT** `
|
||
+ `(Exitcode ${r.code}).` );
|
||
zeilen.push("");
|
||
zeilen.push("Das ist etwas anderes als „kaputt“: Die Datei ist "
|
||
+ "durchgelaufen. Der Zähler hat ihre Punkte nur nicht erkannt – "
|
||
+ "meist, weil sie ihre Ergebnisse anders schreibt als erwartet "
|
||
+ "(` OK ` statt ` ok `, andere Einrückung, eigenes Format). "
|
||
+ "Nachsehen lohnt trotzdem: Eine Datei, deren Punkte niemand "
|
||
+ "zählt, kann still aufhören zu prüfen, ohne dass es auffällt. "
|
||
+ "Zuletzt stand da:");
|
||
zeilen.push("");
|
||
} else if (r.ohneGrund) {
|
||
zeilen.push(`**Konnte nicht sagen, woran es lag** (Exitcode ${r.code}). `
|
||
+ "Das ist etwas anderes als ein Befund: Die Prüfung ist nicht "
|
||
+ "durchgelaufen, hat aber auch keine FEHL-Zeile hinterlassen. "
|
||
+ "Meist steckt dahinter eine Zeitüberschreitung oder ein Absturz "
|
||
+ "im Vorlauf, kein Fehler im Programm. Zuletzt stand da:");
|
||
zeilen.push("");
|
||
}
|
||
zeilen.push(`\`\`\`\n${r.erste}\n\`\`\``);
|
||
zeilen.push("");
|
||
zeilen.push(`Einzeln nachsehen: \`node server/${r.datei}\``);
|
||
zeilen.push("");
|
||
}
|
||
zeilen.push("**Rot heißt nicht automatisch kaputt.** Dreimal am 23.09.2026 war");
|
||
zeilen.push("die Prüfung veraltet und das Programm richtig — einmal war es");
|
||
zeilen.push("umgekehrt und ein Rollenname stand offen im Netz. Erst nachsehen,");
|
||
zeilen.push("dann entscheiden, wer recht hat.");
|
||
} else {
|
||
zeilen.push("Keine Datei ist rot. Der nächste Lauf kommt in der nächsten Nacht.");
|
||
}
|
||
zeilen.push("");
|
||
return zeilen.join("\n") + "\n";
|
||
}
|