Deutsch oeffnet mit „ und schliesst mit “. An 199 Stellen, die ein
Mensch liest, stand als Schlusszeichen ein GERADES " -- „Nächstes"
statt „Nächstes“. Auf sechzehn oeffentlichen Seiten, in vierzig
Dateien des Workspace und in zwoelf Servermodulen, die Texte
verschicken.
Auf dem Bildschirm sieht man den Unterschied sofort. Beim Schreiben
nicht: Das gerade " liegt auf der Tastatur, die anderen nicht.
WARUM EIN ERSTER ANLAUF ZURUECKGENOMMEN WURDE
Ein gerades " ist an vielen Stellen SYNTAX und kein Schriftzeichen --
Grenze einer Zeichenkette, Grenze eines HTML-Attributs, Zeichen in
einem regulaeren Ausdruck. Wer stumpf ersetzt, macht aus
„<a href="https://… ein kaputtes Attribut
/^["'„»\s]+|["'“«.\s]+$/ einen kaputten Ausdruck
"… nichts „mal " + "eben …" eine kaputte Zeichenkette
DIE UNTERSCHEIDUNG LAEUFT AN MERKMALEN, NICHT AN EINER LISTE
< > = dazwischen -> HTML-Marke oder Attribut
endet auf Leerzeichen -> die Zeichenkette hoert hier auf, der
Satz geht in der naechsten Zeile weiter.
Ein deutsches Schlusszeichen steht NIE
hinter einem Leerzeichen.
Rueckstrich mittendrin -> regulaerer Ausdruck
${ ohne } -> mitten in einem Ausdruck
Eine Liste erlaubter Ausnahmen waere die naechste, die niemand
pflegt. Zwoelf Stellen bleiben dadurch stehen, alle zwoelf einzeln
angesehen und alle zwoelf zu Recht -- dort steht das richtige
Schlusszeichen ohnehin weiter unten im Satz.
Fuenf davon waren allerdings ECHTE Fehler HINTER dem Link
(`…>HasiDog</a>".`) -- die erste Regel hatte nur das Attribut
gesehen, nicht den Satz danach. Gezielt nachgezogen.
Ein maskiertes `\"` in workspace-vorlagen.js (28 Hooks) wird zu “ --
ohne Rueckstrich, denn “ begrenzt nichts.
KOMMENTARE BLEIBEN, WIE SIE SIND. Dort liest es niemand ausser mir;
eine Wache, die auch Kosmetik anmahnt, wird weggeklickt. Beim ersten
Messen fielen ausserdem acht Stellen aus buehne.html faelschlich an,
weil `/* */` in HTML (in <style> und <script>) nicht ausgeblendet
war -- jetzt schon.
DIE WACHE DAZU
pruef-struktur prueft es ab sofort mit derselben Regel: 322 Dateien
mit sichtbarem Text, 0 Funde, und die zwoelf bewussten Ausnahmen
werden GEZAEHLT und genannt (erlaubt: 12). Eine Ausnahme, die niemand
sieht, waechst -- und irgendwann steht der echte Fall darin.
GEPRUEFT
pruef-struktur 59 -> 68 Pruefungen, 0 Fehler
node --check auf allen geaenderten JS-Dateien
nachgemessen: 199 geaendert, 12 mit Grund stehen geblieben
gruen geblieben: bewerbung-aufgaben 163, nachwuchs 262,
reaktion 421, support 63, content 45, terminregel 35, treff 85
Und nachgesehen, ob eine Pruefung noch die alte Schreibweise
ERWARTET: 13 Fundstellen, alle dreizehn nur Text in ihrer eigenen
Ausgabe, keine einzige ein Vergleich mit dem Seitentext.
GEGENPROBE: In reaktion.html ein Schlusszeichen zurueckgedreht ->
„workspace/reaktion.html:546 „Nächstes"", mit Datei und Zeile. Und
die Erkennung einzeln gegen HTML-Attribut, fortgesetzte
Zeichenkette, regulaeren Ausdruck und eingesetzten Wert geprueft.
Stempel gesetzt: workspace 670 Verweise in 45 Dateien, oeffentlich
554 in 48 Seiten.
Co-Authored-By: Claude Opus 5 <[email protected]>
335 lines
15 KiB
JavaScript
335 lines
15 KiB
JavaScript
/* =====================================================================
|
||
workspace-befinden-resuemee.js — Eine Zusammenfassung am Ende
|
||
(20.09.2026)
|
||
|
||
Filipe: „und am ende von allen fragen will ich eine gesamte
|
||
zusammenfassung, nicht bei jeder frage oder kategorie eine, sondern
|
||
EINE gesamte am ende. eine motivierende zusammenfassung für die
|
||
person. hol die beste der beste möglichkeiten dafür die man beutzen
|
||
kann kostenlos."
|
||
|
||
---------------------------------------------------------------------
|
||
DIE FRAGE NACH DER „BESTEN KOSTENLOSEN MÖGLICHKEIT" — EHRLICH
|
||
BEANTWORTET
|
||
|
||
Naheliegend wäre eine der kostenlosen KI-Schnittstellen (Groq,
|
||
Gemini, HuggingFace). Sie wäre hier die FALSCHE Wahl, und zwar aus
|
||
drei Gründen, von denen jeder einzelne reicht:
|
||
|
||
1. ES SIND GESUNDHEITSDATEN. Die Antworten sagen, wer nachts wach
|
||
liegt, wer angefeindet wird, wer ans Aufhören denkt. Genau
|
||
deshalb sind sie im ganzen Haus als `nurSelbst` markiert und
|
||
liegen in einer eigenen Tabelle, die kein anderer Weg liest.
|
||
Sie an einen fremden Dienst zu schicken, damit er einen netten
|
||
Satz daraus baut, würde diese ganze Sorgfalt in einer Zeile
|
||
aufheben. Kostenlose KI-Angebote bezahlt man mit den Daten —
|
||
in ihren Bedingungen steht meist ausdrücklich, dass Eingaben
|
||
zum Training verwendet werden dürfen.
|
||
|
||
2. EIN SPRACHMODELL ERFINDET. Es schreibt ohne Weiteres „du hast
|
||
dich diesen Monat deutlich erholt", wenn das Gegenteil dasteht,
|
||
und es klingt dabei überzeugend. Eine Zusammenfassung über das
|
||
eigene Befinden, die etwas Falsches behauptet, ist schlimmer
|
||
als keine: Man glaubt ihr ja, und beim nächsten Mal glaubt man
|
||
ihr nicht mehr — auch dann nicht, wenn sie recht hat.
|
||
|
||
3. SIE FÄLLT AUS. Ein fremder Dienst hat Störungen, Kontingente
|
||
und Kündigungen. Dieser Text hier funktioniert in zehn Jahren
|
||
noch, ohne Schlüssel, ohne Rechnung, ohne Netz.
|
||
|
||
DIE BESTE KOSTENLOSE MÖGLICHKEIT IST DESHALB KEINE KI, SONDERN
|
||
SORGFÄLTIG GESCHRIEBENE SPRACHE ÜBER GERECHNETEN ZAHLEN. Sie läuft
|
||
auf Filipes eigenem Server, kostet nichts, lügt nie und kann nicht
|
||
ausfallen. Was ein Sprachmodell hier besser könnte — Abwechslung im
|
||
Wortlaut — ist gegen die drei Punkte oben kein Argument.
|
||
|
||
---------------------------------------------------------------------
|
||
WIE MAN MOTIVIERT, OHNE ZU LÜGEN
|
||
|
||
Das ist die eigentliche Schwierigkeit. Eine Zusammenfassung, die bei
|
||
sechs von acht „da hakt es" fröhlich bleibt, ist keine Ermutigung,
|
||
sondern Hohn — und beim nächsten Mal antwortet man ihr nicht mehr
|
||
ehrlich. Drei Regeln, und sie gelten in jeder Lage:
|
||
|
||
ZUERST, WAS TRÄGT — und zwar benannt, nicht als Zahl. „Dass du
|
||
nach der Schicht abschalten kannst, ist das, was das hier
|
||
zusammenhält" sagt etwas. „5 von 8 grün" sagt nichts.
|
||
|
||
DANN HÖCHSTENS EINE SACHE. Wer fünf Probleme auf einmal vorgelegt
|
||
bekommt, sieht keine fünf Aufgaben, sondern einen Berg. Die Liste
|
||
aller Punkte steht ohnehin darüber; hier steht die EINE, bei der
|
||
sich das Hinsehen am meisten lohnt.
|
||
|
||
UND EIN SCHLUSS, DER ZUR LAGE PASST. Nicht „weiter so" für alle.
|
||
Wer gerade viel trägt, braucht einen anderen letzten Satz als
|
||
jemand, bei dem alles läuft.
|
||
|
||
---------------------------------------------------------------------
|
||
KEINE ZAHLEN, DIE JEMAND PFLEGEN MUSS
|
||
|
||
Jede Zahl in diesem Text wird gerechnet — auch „acht Fragen" und
|
||
„sechs Felder". Ein fester Satz „alle sechs Felder sind ruhig" wäre
|
||
beim siebten Feld still falsch, und niemand würde es merken.
|
||
===================================================================== */
|
||
|
||
/** Welche Stufe zählt als „läuft", welche als „hakt".
|
||
*
|
||
* Abgeleitet aus dem Schlüssel und nicht als eigene Liste gepflegt:
|
||
* `unklar` ist ausdrücklich WEDER gut noch schlecht — „kann ich nicht
|
||
* sagen" ist eine ehrliche Antwort und darf nicht als halbes Problem
|
||
* gezählt werden. Wer sie zu den Sorgen schlägt, erzeugt Sorgen, die
|
||
* niemand geäußert hat. */
|
||
const TRAEGT = new Set(["laeuft"]);
|
||
const WAECHST = new Set(["waechst"]);
|
||
const HAKT = new Set(["hakt"]);
|
||
|
||
/** Ein Satzende ohne doppelten Punkt. */
|
||
const satz = (t) => {
|
||
const x = String(t || "").trim();
|
||
if (!x) return "";
|
||
return /[.!?…]$/.test(x) ? x : x + ".";
|
||
};
|
||
|
||
/** Der erste Buchstabe klein — für Halbsätze, die angehängt werden.
|
||
* (Deutsche Substantive bleiben groß; deshalb nur dort benutzt, wo
|
||
* ein Verb oder Pronomen folgt.) */
|
||
const klein = (t) => (t ? t[0].toLowerCase() + t.slice(1) : t);
|
||
|
||
/**
|
||
* Die eine Zusammenfassung am Ende einer Runde.
|
||
*
|
||
* @param antworten { schluessel: stufe } — die Antworten DIESER Runde
|
||
* @param punkte Die Fragen dieser Runde (mit titel, feld, kern)
|
||
* @param felder BEFINDEN_FELDER
|
||
* @param vorher Die Antworten der VORIGEN Runde, oder null
|
||
* @returns {{moeglich, kopf, traegt, thema, schluss, zahlen}}
|
||
*/
|
||
export function resuemeeVon(antworten, punkte, felder, vorher = null, einordnung = {}) {
|
||
const gefragt = punkte.filter((p) => antworten?.[p.schluessel]);
|
||
|
||
/* DRITTER AUSGANG. Ohne Antworten gibt es nichts zusammenzufassen --
|
||
und das muss dastehen, statt dass ein freundlicher Satz über
|
||
nichts erscheint. */
|
||
if (!gefragt.length) {
|
||
return {
|
||
moeglich: false,
|
||
grund: "Noch keine Antworten in dieser Runde.",
|
||
kopf: null, traegt: [], thema: null, schluss: null,
|
||
zahlen: { gefragt: 0, traegt: 0, waechst: 0, hakt: 0, unklar: 0, felder: 0 },
|
||
};
|
||
}
|
||
|
||
const mit = (menge) => gefragt.filter((p) => menge.has(antworten[p.schluessel]));
|
||
const traegtP = mit(TRAEGT);
|
||
const waechstP = mit(WAECHST);
|
||
const haktP = mit(HAKT);
|
||
const unklarP = gefragt.length - traegtP.length - waechstP.length - haktP.length;
|
||
|
||
const feldName = (k) => felder.find((f) => f.schluessel === k)?.name || k;
|
||
|
||
/* ---- WELCHES FELD IST DAS THEMA? --------------------------------
|
||
Nicht die erste Frage mit „hakt", sondern das FELD mit den
|
||
meisten. Drei Haken quer über alles heißen „schwerer Monat";
|
||
drei Haken in EINEM Feld heißen „hier ist etwas". Nur das zweite
|
||
ist ein Befund, und nur darüber lohnt ein Satz.
|
||
|
||
Bei Gleichstand entscheidet die Reihenfolge der Felder, und die
|
||
geht von außen nach innen — das schwerere Thema gewinnt. */
|
||
const proFeld = new Map();
|
||
for (const p of haktP) {
|
||
proFeld.set(p.feld, (proFeld.get(p.feld) || 0) + 1);
|
||
}
|
||
let themaFeld = null, themaN = 0;
|
||
for (const f of felder) {
|
||
const n = proFeld.get(f.schluessel) || 0;
|
||
if (n > themaN) { themaFeld = f; themaN = n; }
|
||
}
|
||
|
||
/* ---- WAS TRÄGT ---------------------------------------------------
|
||
Höchstens zwei, und benannt. Eine Aufzählung von fünf Dingen liest
|
||
niemand; zwei bleiben hängen. Kernfragen zuerst — sie sind die,
|
||
die in jeder Runde gestellt werden, und damit die, deren „läuft"
|
||
am meisten bedeutet. */
|
||
const traegtListe = [...traegtP]
|
||
.sort((a, b) => (b.kern ? 1 : 0) - (a.kern ? 1 : 0))
|
||
.slice(0, 2)
|
||
.map((p) => ({ schluessel: p.schluessel, titel: p.titel, feld: p.feld }));
|
||
|
||
/* ---- HAT SICH SEIT DEM LETZTEN MAL ETWAS GEBESSERT? -------------
|
||
Nur echte Verbesserungen, und nur bei Fragen, die BEIDE Male
|
||
gestellt wurden. Seit die Fragen rotieren, wäre „war letztes Mal
|
||
nicht dabei" sonst eine Verbesserung — und das ist keine. */
|
||
const besser = [];
|
||
if (vorher) {
|
||
const rang = { hakt: 0, waechst: 1, laeuft: 2 };
|
||
for (const p of gefragt) {
|
||
const a = rang[vorher[p.schluessel]];
|
||
const b = rang[antworten[p.schluessel]];
|
||
if (a !== undefined && b !== undefined && b > a) besser.push(p);
|
||
}
|
||
}
|
||
|
||
/* ---- DER KOPFSATZ ------------------------------------------------
|
||
Vier Lagen, vier Sätze — und keiner davon behauptet etwas, das
|
||
nicht in den Zahlen steht. */
|
||
const n = gefragt.length;
|
||
let kopf;
|
||
if (!haktP.length && !waechstP.length) {
|
||
kopf = unklarP === n
|
||
? "Diesmal konntest du zu fast allem nichts sagen. Das ist eine ehrliche "
|
||
+ "Antwort – und beim nächsten Mal weißt du vermutlich mehr."
|
||
: `Alle ${n} Fragen dieser Runde stehen auf „Läuft“. Das ist selten, und `
|
||
+ "es ist kein Zufall – irgendwas machst du gerade richtig.";
|
||
} else if (!haktP.length) {
|
||
kopf = `Nichts hakt gerade. ${waechstP.length === 1 ? "Eine Sache wächst" : `${waechstP.length} Sachen wachsen`} `
|
||
+ "noch – das heißt, sie sind unterwegs, nicht liegengeblieben.";
|
||
} else if (haktP.length === 1) {
|
||
kopf = `Von ${n} Fragen hakt genau eine. ${n - 1} laufen oder wachsen – `
|
||
+ "das ist der Boden, auf dem du stehst, und der ist tragfähig.";
|
||
} else if (haktP.length < n / 2) {
|
||
kopf = `${haktP.length} von ${n} Sachen haken gerade. Die anderen ${n - haktP.length} `
|
||
+ "nicht – und das ist die Hälfte der Auskunft, die man beim Draufschauen "
|
||
+ "meistens übersieht.";
|
||
} else {
|
||
/* DIE SCHWERE LAGE. Hier NICHT aufmunternd werden -- das ist der
|
||
Moment, in dem eine fröhliche Zusammenfassung alles Vertrauen
|
||
kostet. Stattdessen: benennen, entlasten, und den Weg zeigen.
|
||
„Fast nie an dir" ist keine Beschönigung, sondern das, was die
|
||
Forschung zu Belastung in Ehrenämtern durchgehend findet. */
|
||
kopf = `${haktP.length} von ${n} Sachen haken. Das ist viel, und es liegt `
|
||
+ "fast nie an der Person, die es aushält. Es ist ein guter Moment, "
|
||
+ "das jemandem zu sagen, statt es noch eine Runde mitzunehmen.";
|
||
}
|
||
|
||
/* ---- DER SCHLUSSSATZ ---------------------------------------------
|
||
Er gehört zur Lage. „Weiter so" für alle wäre ein Satz, der nie
|
||
jemanden meint. */
|
||
let schluss;
|
||
if (besser.length) {
|
||
schluss = besser.length === 1
|
||
? `Und etwas hat sich seit dem letzten Mal gebessert: ${klein(besser[0].titel)}. `
|
||
+ "Das hat sich nicht von allein gemacht."
|
||
: `Und ${besser.length} Sachen haben sich seit dem letzten Mal gebessert. `
|
||
+ "Das hat sich nicht von allein gemacht.";
|
||
} else if (haktP.length >= n / 2) {
|
||
schluss = "Du musst das hier nicht allein lösen. Dass du es aufgeschrieben "
|
||
+ "hast, ist der erste Teil – der zweite ist ein Satz an jemanden.";
|
||
} else if (haktP.length) {
|
||
schluss = "Das musst du heute nicht lösen. Es reicht, dass du es weißt – "
|
||
+ "beim nächsten Mal siehst du, ob es geblieben ist.";
|
||
} else {
|
||
schluss = "Nächste Runde in zwei Wochen. Bis dahin: nichts zu tun.";
|
||
}
|
||
|
||
/* ---- EIN SCHRITT, NICHT NEUN (22.09.2026) ------------------------
|
||
Filipe: "das resume unten auf jeder frage hab ich auch schon
|
||
mehrmals gesagt muss weg ich will ein ganzes gesamtresume ...
|
||
aber auch individuel so dass jeder sich selber auch verbessert
|
||
und steigert fuers team."
|
||
|
||
Vorher stand unter JEDER Frage eine Einordnung und ein "Was
|
||
hilft" -- bei neun Fragen neun Ratschlaege auf einmal. Das ist
|
||
keine Hilfe, sondern eine Maengelliste mit Fussnoten. Wer neun
|
||
Dinge gleichzeitig angehen soll, geht keins an.
|
||
|
||
Jetzt genau EINER: der zu der Sache, die am meisten haengt.
|
||
Kernfragen zuerst -- sie werden in jeder Runde gestellt, ihr
|
||
"hakt" wiegt also schwerer als das einer Zusatzfrage, die alle
|
||
vier Runden vorbeikommt.
|
||
|
||
Und wenn nichts hakt, ist der Schritt trotzdem einer: dann der
|
||
zu der Sache, die noch WAECHST. "Nichts zu tun" ist fuer jemanden,
|
||
bei dem alles laeuft, der richtige Satz -- aber nur dann. */
|
||
const schrittKandidaten = haktP.length ? haktP : waechstP;
|
||
const schrittP = [...schrittKandidaten]
|
||
.sort((a, b) => (b.kern ? 1 : 0) - (a.kern ? 1 : 0))[0] || null;
|
||
const e = schrittP ? (einordnung[schrittP.schluessel] || {}) : {};
|
||
const schritt = schrittP && e.hilft
|
||
? {
|
||
titel: schrittP.titel,
|
||
was: e.hilft,
|
||
/* Die Einordnung aus der Forschung steht EINMAL da, nicht
|
||
neunmal -- als Grund, warum dieser Schritt der richtige ist.
|
||
Ohne sie waere es ein Ratschlag; mit ihr ist es einer, den
|
||
jemand nachgeprueft hat. */
|
||
welt: e.welt || null,
|
||
haengt: !!haktP.length,
|
||
}
|
||
: null;
|
||
|
||
/* ---- WAS DAS TEAM DAVON HAT --------------------------------------
|
||
Filipe: "mega geile super team loesungen findet aber auch
|
||
individuel".
|
||
|
||
DER SATZ WIRD ABGELEITET, NICHT ERFUNDEN. Ein Team-Spruch, der
|
||
immer derselbe ist, wird beim dritten Mal ueberlesen -- und ein
|
||
"gemeinsam schaffen wir das" an jemanden, bei dem gerade fuenf
|
||
von neun Sachen haken, ist das Gegenteil von Hilfe.
|
||
|
||
Vier Lagen, vier verschiedene Saetze. Und in der schwersten ist
|
||
der Team-Bezug KEINE Aufforderung, sondern eine Entlastung: Wer
|
||
viel traegt, braucht nicht zu hoeren, dass er auch noch etwas
|
||
fuers Team tun soll. */
|
||
let team;
|
||
if (haktP.length >= n / 2 && n > 0) {
|
||
team = "Im Team ist gerade nichts von dir gefragt. Wenn du magst, sag "
|
||
+ "einem Menschen, wie es steht – das ist alles, und es reicht.";
|
||
} else if (haktP.length) {
|
||
team = themaFeld && themaN > 1
|
||
? `Wahrscheinlich bist du damit nicht allein: „${feldName(themaFeld.schluessel)}“ `
|
||
+ "hakt selten nur bei einem. Es in der Runde anzusprechen löst es oft "
|
||
+ "für mehrere auf einmal."
|
||
: "Eine Sache, die bei dir hakt, hakt selten nur bei dir. Wer sie ausspricht, "
|
||
+ "löst sie oft für mehr als sich selbst.";
|
||
} else if (traegtListe.length) {
|
||
team = `Was bei dir gerade trägt – ${klein(traegtListe[0].titel)} – ist genau das, `
|
||
+ "wonach jemand anderes gerade sucht. Zwei Sätze darüber in der Runde sind "
|
||
+ "mehr wert als jede Anleitung.";
|
||
} else {
|
||
team = "Wenn du in der nächsten Runde etwas merkst, das anderen helfen würde: "
|
||
+ "sag es. Das ist der kürzeste Weg, wie aus einzelnen Leuten ein Team wird.";
|
||
}
|
||
|
||
return {
|
||
moeglich: true,
|
||
kopf,
|
||
schritt,
|
||
team,
|
||
traegt: traegtListe,
|
||
thema: themaFeld && themaN > 0
|
||
? {
|
||
feld: themaFeld.schluessel,
|
||
name: feldName(themaFeld.schluessel),
|
||
anzahl: themaN,
|
||
/* MEHRERE IN EINEM FELD SIND ETWAS ANDERES ALS EINES. Der Satz
|
||
sagt, welches von beidem es ist -- daran hängt, ob man mit
|
||
einer Sache spricht oder mit einem Thema. */
|
||
satz: themaN > 1
|
||
? satz(`Wenn du nur an einer Stelle hinschaust, dann hier: „${feldName(themaFeld.schluessel)}“ `
|
||
+ `– da stehen ${themaN} der Haken beieinander. Das ist selten Zufall`)
|
||
: satz(`Wenn du nur an einer Stelle hinschaust, dann hier: „${feldName(themaFeld.schluessel)}“`),
|
||
/* DIE TITEL NUR, WENN ES MEHRERE SIND (22.09.2026).
|
||
Bei genau einem stand er zweimal untereinander: einmal hier
|
||
und gleich darunter als "Dein naechster Schritt". Im Bild
|
||
sah das aus wie ein Fehler -- gefunden hat es kein Messwert,
|
||
sondern der Blick auf den Bildschirm. */
|
||
titel: themaN > 1
|
||
? haktP.filter((p) => p.feld === themaFeld.schluessel).map((p) => p.titel)
|
||
: [],
|
||
}
|
||
: null,
|
||
schluss,
|
||
zahlen: {
|
||
gefragt: n,
|
||
traegt: traegtP.length,
|
||
waechst: waechstP.length,
|
||
hakt: haktP.length,
|
||
unklar: unklarP,
|
||
besser: besser.length,
|
||
/* GERECHNET, nicht eingetragen: Wer ein siebtes Feld ergänzt,
|
||
bekommt hier automatisch die 7. */
|
||
felder: new Set(gefragt.map((p) => p.feld).filter(Boolean)).size,
|
||
},
|
||
};
|
||
}
|