diff --git a/server/crew-adresse.js b/server/crew-adresse.js index ade8c7f0..ff753e65 100644 --- a/server/crew-adresse.js +++ b/server/crew-adresse.js @@ -214,12 +214,46 @@ export function istWorkspacePfad(pfad) { die jede Wand im Browser ansehen, keine mehr oeffnen -- sie blieben gruen und wuerden nichts mehr messen. Ein gruener Lauf, der nichts mehr prueft, ist schlimmer als ein roter. */ -/* EINE benannte Wand, seit der Treff keine eigene mehr hat. Die Tafel - bleibt trotzdem eine Tafel und wird nicht wieder zum Sonderfall: Sie - ist der Ort, an dem die naechste Wand eingetragen wird, ohne dass - jemand drei Bedingungen umbauen muss. */ -const BENANNTE_WAENDE = [ +/* Die Tafel bleibt eine Tafel und wird nicht wieder zum Sonderfall: + Sie ist der Ort, an dem der naechste Eintrag dazukommt, ohne dass + jemand drei Bedingungen umbauen muss. + + --------------------------------------------------------------------- + SEIT DEM 21.09.2026 STEHT HIER AUCH EINE STILVORLAGE + + Im Kopf von crew-haus.css stand seit dem 10.09.: "Diese Datei wird + NUR auf crew.dogfather-universe.com ausgeliefert." Nachgemessen war + das falsch -- 27 806 Byte, HTTP 200, von der Agenturadresse, OHNE + Anmeldung. Die Weiche biegt `haus.css` auf diesen Namen um; wer den + Namen direkt nennt, kam an der Weiche vorbei. + + Es war also wieder derselbe Befund: Ein Kommentar, der eine Absicht + beschreibt, erfuellt sie nicht. Und es war dieselbe Luecke wie bei + der Zugangswand am 10.09. -- express.static liefert jede Datei aus, + die man beim Namen nennt. + + WAS DARIN STAND: das ganze Farbhaus von Team Dogi, mit seinen + Kommentaren -- darunter zweimal die Crew-Adresse im Klartext. Die + Adresse ist das eigentlich Verborgene; die Rollennamen stehen auf + der Zugangswand dort ohnehin offen (so gewollt: "3 rollen. + dogfather. rechte hand und modis"). Wer die Adresse hat, sieht sie. + Wer sie nicht hat, sollte sie nicht aus einer Stilvorlage der + Agenturseite erfahren. + + DIE INTERNE UMBIEGUNG BLEIBT UNBERUEHRT, und das ist der Grund, + warum dieser Eintrag ueberhaupt funktioniert: Diese Tafel wird in + crewWeiche() ganz oben gefragt (Zeile ~323), die Umbiegung von + `haus.css` passiert weiter unten (~454). Auf crew. wird also nach + wie vor umgebogen -- dort ist die Datei zuhause --, und auf der + Agenturadresse gibt es sie beim Namen nicht mehr. + + Nachgeprueft in pruef-crew-adresse: einmal, dass sie auf crew. + ankommt, und einmal, dass die Agenturadresse 404 sagt. Ohne die + zweite Haelfte waere der Eintrag eine Behauptung wie die, die er + ersetzt. */ +export const GEHOERT_ZU_ADRESSE = [ { datei: "/workspace/crew-index.html", zuhause: istCrewAdresse }, + { datei: "/workspace/assets/css/crew-haus.css", zuhause: istCrewAdresse }, ]; /** Wird hier eine Wand verlangt, die zu einer ANDEREN Tuer gehoert? */ @@ -229,7 +263,7 @@ export function istFremdeWand(hostKopfzeile, pfad) { zieht sie danach selbst zusammen und liefert die Datei aus. Genau daran ist am 04.09.2026 die Schranke des Workspace vorbeigelaufen. */ const p = String(pfad || "").toLowerCase().replace(/\/{2,}/g, "/"); - const wand = BENANNTE_WAENDE.find((w) => w.datei === p); + const wand = GEHOERT_ZU_ADRESSE.find((w) => w.datei === p); if (!wand) return false; if (istPruefAdresse(hostKopfzeile)) return false; const benannt = istOhneModiAdresse(hostKopfzeile) || istCrewAdresse(hostKopfzeile); diff --git a/server/helfer-ohne-kommentar.mjs b/server/helfer-ohne-kommentar.mjs new file mode 100644 index 00000000..ee89c47b --- /dev/null +++ b/server/helfer-ohne-kommentar.mjs @@ -0,0 +1,249 @@ +/* ===================================================================== + helfer-ohne-kommentar.mjs — Kommentare entfernen, ohne den Rest zu + beschaedigen (21.09.2026) + + --------------------------------------------------------------------- + WOFUER + + Filipes Entscheidung vom 21.09.2026 zum Rollennamen der Community: + Er darf nicht in CODE und nicht in SICHTBAREN TEXTEN stehen -- in + Kommentaren schon. Die Begruendung ist messbar: Auf der Zugangswand + von Team Dogi stehen die drei Rollennamen ohnehin offen ("3 rollen. + dogfather. rechte hand und modis"), und geschuetzt wird der Zugang + durch den Code, nicht durch ein Wort. Eine Pruefung, die 45 + Kommentare anmahnt, waere eine Dauer-Rotmeldung -- und Rot, das + immer da ist, wird ueberlesen. + + Damit diese Regel ueberhaupt pruefbar ist, muss jemand sagen + koennen, was Kommentar ist und was nicht. Genau das tut diese Datei. + + --------------------------------------------------------------------- + WARUM KEIN EINFACHER SUCHEN-UND-ERSETZEN-AUSDRUCK + + Ein Muster, das einfach vom naechsten Sternchen-Schraegstrich bis + zum uebernaechsten loescht, sieht richtig aus und ist es nicht. + Es zerschneidet zum Beispiel eine Adresse, in der dieselbe + Zeichenfolge mitten in einer Zeichenkette steht, oder ein + Suchmuster, das Schraegstrich und Sternchen als Zeichen + enthaelt -- und macht aus gueltigem Programmtext Unsinn. + + (Beide Beispiele stehen hier bewusst NICHT ausgeschrieben: Ein + Kommentar, der die Zeichenfolge enthaelt, mit der ein Kommentar + endet, beendet sich selbst. Beim ersten Versuch ist genau das + passiert -- in der Datei, die davor warnt.) + + IM HAUS IST DIESE FALLE SCHON EINMAL ZUGESCHNAPPT: Am 20.09.2026 hat ein Ausdruck beim + Umstellen der Portnummern `", port: 4231, path: "` fuer eine + Zeichenkette gehalten. Das Ergebnis war gueltiges JavaScript ohne + das Feld `port` -- `node --check` sagte bei allen 164 Dateien "in + Ordnung", und aufgefallen ist es erst, weil ein Lauf gegen einen + GEMESSENEN Vorstand verglichen wurde. + + Deshalb wird hier Zeichen fuer Zeichen gelesen und mitgefuehrt, wo + man gerade steht: in Programmtext, in einer Zeichenkette, in einer + Vorlage, in einem Suchmuster oder in einem Kommentar. + + --------------------------------------------------------------------- + DAS SCHWIERIGE STUECK: IST `/` EIN TEILEN ODER EIN SUCHMUSTER + + `a / b` teilt, `/a/.test(x)` sucht. Unterscheiden kann man das nur + am ZEICHEN DAVOR: Nach einem Wert (Zahl, Name, schliessende Klammer) + teilt es; nach einem Operator, einem Komma oder einer oeffnenden + Klammer beginnt ein Suchmuster. Das ist die Regel, die auch + Browser benutzen, und sie ist fuer diesen Zweck genau genug. + + WAS SIE NICHT KANN: `return /x/` gegen `a++ /x/` unterscheiden, + wenn davor ein Schluesselwort steht, das wie ein Name aussieht. + Deshalb gelten `return`, `typeof`, `case`, `in`, `of`, `new`, + `delete`, `void`, `do` und `else` ausdruecklich als Operator. Die + Liste steht unten und ist abgezaehlt, nicht geraten. + + --------------------------------------------------------------------- + WAS ZURUECKKOMMT + + Derselbe Text, gleich lang, mit Leerzeichen an den Stellen, an denen + Kommentar stand. GLEICH LANG ist Absicht: So stimmen Zeilennummern + und Spalten weiterhin, und ein Fund laesst sich ohne Umrechnung in + der Originaldatei zeigen. Zeilenumbrueche bleiben deshalb erhalten. + ===================================================================== */ + +/* Nach diesen Woertern beginnt ein `/` ein Suchmuster und kein Teilen. + Abgezaehlt, nicht geraten -- siehe Kopf. */ +const VOR_SUCHMUSTER = new Set([ + "return", "typeof", "case", "in", "of", "new", "delete", "void", + "do", "else", "yield", "await", "instanceof", +]); + +/** Ersetzt jeden Kommentar in JavaScript durch Leerzeichen. + * + * Zeichenketten, Vorlagen und Suchmuster bleiben unangetastet -- + * auch wenn `//` oder darin steht. */ +export function jsOhneKommentar(quelle) { + const t = String(quelle); + let aus = ""; + let i = 0; + /* Das letzte Zeichen, das kein Leerraum war. Daran haengt die + Entscheidung Teilen-oder-Suchmuster. */ + let letztes = ""; + let letztesWort = ""; + + const schreibe = (s) => { aus += s; }; + const leeren = (s) => { aus += s.replace(/[^\n]/g, " "); }; + + while (i < t.length) { + const c = t[i]; + const d = t[i + 1]; + + /* ---- Kommentare ------------------------------------------------ */ + if (c === "/" && d === "/") { + let j = i; + while (j < t.length && t[j] !== "\n") j++; + leeren(t.slice(i, j)); + i = j; + continue; + } + if (c === "/" && d === "*") { + let j = t.indexOf("*/", i + 2); + j = j === -1 ? t.length : j + 2; + leeren(t.slice(i, j)); + i = j; + continue; + } + + /* ---- Zeichenketten --------------------------------------------- */ + if (c === '"' || c === "'" || c === "`") { + let j = i + 1; + while (j < t.length) { + if (t[j] === "\\") { j += 2; continue; } + if (t[j] === c) { j++; break; } + /* In einer Vorlage kann `${…}` wieder Programmtext sein. Fuer + den Zweck hier -- Kommentare finden -- reicht es, das Stueck + als Ganzes stehen zu lassen: Ein Kommentar darin waere + ohnehin Programmtext und soll nicht verschwinden. */ + j++; + } + schreibe(t.slice(i, j)); + letztes = c; letztesWort = ""; + i = j; + continue; + } + + /* ---- Suchmuster ------------------------------------------------ */ + if (c === "/") { + const wertDavor = /[\w$)\]]/.test(letztes) && !VOR_SUCHMUSTER.has(letztesWort); + if (!wertDavor) { + let j = i + 1; + let inKlasse = false; + let fertig = false; + while (j < t.length) { + const z = t[j]; + if (z === "\\") { j += 2; continue; } + if (z === "\n") break; /* unfertig -- dann war es Teilen */ + if (z === "[") inKlasse = true; + else if (z === "]") inKlasse = false; + else if (z === "/" && !inKlasse) { j++; fertig = true; break; } + j++; + } + if (fertig) { + while (j < t.length && /[a-z]/.test(t[j])) j++; /* Flaggen */ + schreibe(t.slice(i, j)); + letztes = "/"; letztesWort = ""; + i = j; + continue; + } + } + } + + /* ---- alles andere ---------------------------------------------- */ + schreibe(c); + if (!/\s/.test(c)) { + letztes = c; + if (/[\w$]/.test(c)) letztesWort += c; + else letztesWort = ""; + } + i++; + } + return aus; +} + +/** CSS kennt nur die Block-Form und Zeichenketten. */ +export function cssOhneKommentar(quelle) { + const t = String(quelle); + let aus = ""; + let i = 0; + while (i < t.length) { + const c = t[i]; + if (c === "/" && t[i + 1] === "*") { + let j = t.indexOf("*/", i + 2); + j = j === -1 ? t.length : j + 2; + aus += t.slice(i, j).replace(/[^\n]/g, " "); + i = j; + continue; + } + if (c === '"' || c === "'") { + let j = i + 1; + while (j < t.length) { + if (t[j] === "\\") { j += 2; continue; } + if (t[j] === c) { j++; break; } + j++; + } + aus += t.slice(i, j); + i = j; + continue; + } + aus += c; + i++; + } + return aus; +} + +/** HTML: heraus, und die eingebetteten ", endung: ".html", soll: false, satz: "ein Kommentar IM eingebetteten Skript auch nicht" }, + { text: "", endung: ".html", soll: true, satz: "eine Zeichenkette im eingebetteten Skript schon" }, + { text: "", endung: ".html", soll: false, satz: "und im eingebetteten Stil ebenso wenig" }, + ]; + let richtig = 0; + for (const pr of PROBEN) { + const rest = ohneKommentar(pr.text, pr.endung); + const gefunden = MUSTER.test(rest); + if (gefunden === pr.soll) richtig++; + else ok(false, `${pr.satz} -- ABER die Probe lief falsch: ${JSON.stringify(pr.text)}`); + } + /* DIE ZAHL GEHOERT IN DIE BEDINGUNG. "alle richtig" waere auch bei + einer leeren Liste wahr. */ + ok(richtig === PROBEN.length && PROBEN.length === 13, + `alle ${richtig} von ${PROBEN.length} Proben treffen zu`); + + /* Und die Laenge bleibt gleich -- daran haengen die Zeilennummern, + mit denen ein Fund oben gemeldet wird. Waere sie es nicht, zeigte + die Meldung auf die falsche Zeile, und wer nachsieht, findet + nichts und glaubt der Pruefung nicht mehr. */ + const roh = "var a = 1; /* Modi */\nvar b = 2; // Modi\nvar c = 3;"; + const ohne = ohneKommentar(roh, ".js"); + ok(ohne.length === roh.length, + `der Text bleibt zeichengenau gleich lang (${ohne.length} von ${roh.length})`); + ok(ohne.split("\n").length === roh.split("\n").length, + `und behaelt seine ${roh.split("\n").length} Zeilen`); +} + console.log(`\n${fehler ? "FEHLGESCHLAGEN" : "BESTANDEN"} -- ${geprueft} geprueft, ${fehler} Fehler`); process.exit(fehler ? 1 : 0); diff --git a/server/pruef-team-ampel.mjs b/server/pruef-team-ampel.mjs index 3964ecad..f29be1e7 100644 --- a/server/pruef-team-ampel.mjs +++ b/server/pruef-team-ampel.mjs @@ -129,7 +129,7 @@ const antwort = await holeLage(keksDogi); ok(antwort.status === 200, `die Lage kommt (${antwort.status})`); const roh = await antwort.text(); const lage = JSON.parse(roh); -const leute = lage.modis || []; +const leute = lage.leute || []; /* ===================================================================== 1. KOMMT KLARTEXT DURCH? @@ -196,11 +196,11 @@ console.log("--- Die Grenze bei drei Stunden ---"); const idGrenze = anlegen("Grete", "modi", "CODE-MODI-0004"); termin(idGrenze, 179, 10); -const g1 = ((await (await holeLage(keksDogi)).json()).modis || []).find((m) => m.id === idGrenze); +const g1 = ((await (await holeLage(keksDogi)).json()).leute || []).find((m) => m.id === idGrenze); ok(g1?.verfuegbar?.[0]?.ampel === "teils", `179 Minuten sind "teils" (${g1?.verfuegbar?.[0]?.ampel})`); d.prepare("UPDATE termine SET dauer_min = 180 WHERE creator_id = ?").run(idGrenze); -const g2 = ((await (await holeLage(keksDogi)).json()).modis || []).find((m) => m.id === idGrenze); +const g2 = ((await (await holeLage(keksDogi)).json()).leute || []).find((m) => m.id === idGrenze); ok(g2?.verfuegbar?.[0]?.ampel === "voll", `180 Minuten sind "voll" (${g2?.verfuegbar?.[0]?.ampel})`); /* ===================================================================== @@ -218,7 +218,7 @@ for (const id of [idFrei, idTeils, idVoll, idGrenze]) { const nachRegel = d.prepare( `SELECT COUNT(*) AS n FROM termine t WHERE (${regel.wo}) AND date(t.beginn) = ?`) .get(...regel.werte, heute)?.n || 0; - const lage2 = ((await (await holeLage(keksDogi)).json()).modis || []).find((m) => m.id === id); + const lage2 = ((await (await holeLage(keksDogi)).json()).leute || []).find((m) => m.id === id); const nachAmpel = lage2?.verfuegbar?.[0]?.termine ?? -1; if (nachRegel === nachAmpel) gleich++; else ungleich++; } @@ -234,7 +234,7 @@ console.log("--- Wer die Ampel sieht ---"); const handLage = await holeLage(keksHand); ok(handLage.status === 200, `die rechte Hand sieht die Lage (${handLage.status})`); const handRoh = await handLage.text(); -ok(JSON.parse(handRoh).modis?.[0]?.verfuegbar?.length === 7, +ok(JSON.parse(handRoh).leute?.[0]?.verfuegbar?.length === 7, "und die Ampel gehoert dazu -- sie koordiniert das Team"); for (const wort of GEHEIM) { ok(!handRoh.includes(wort), `auch bei ihr steht "${wort.slice(0, 18)}…" nicht drin`); @@ -251,12 +251,12 @@ console.log(""); console.log("--- Gegenprobe: ein Termin veraendert die Farbe ---"); termin(idFrei, 300, 9); -const nachher = ((await (await holeLage(keksDogi)).json()).modis || []).find((m) => m.id === idFrei); +const nachher = ((await (await holeLage(keksDogi)).json()).leute || []).find((m) => m.id === idFrei); ok(nachher?.verfuegbar?.[0]?.ampel === "voll", `aus "frei" wird "voll" (${nachher?.verfuegbar?.[0]?.ampel})`); d.prepare("DELETE FROM termine WHERE creator_id = ?").run(idFrei); -const zurueck = ((await (await holeLage(keksDogi)).json()).modis || []).find((m) => m.id === idFrei); +const zurueck = ((await (await holeLage(keksDogi)).json()).leute || []).find((m) => m.id === idFrei); ok(zurueck?.verfuegbar?.[0]?.ampel === "frei", `und zurueckgenommen wieder "frei" (${zurueck?.verfuegbar?.[0]?.ampel})`); diff --git a/server/pruef-team-stufen.mjs b/server/pruef-team-stufen.mjs index cdc8e5fc..7f04c8a4 100644 --- a/server/pruef-team-stufen.mjs +++ b/server/pruef-team-stufen.mjs @@ -118,7 +118,7 @@ console.log("--- Die Mitglieder-Kachel (Kapitel 4) ---"); const a1 = await holeLage(keksDogi); ok(a1.status === 200, `DogFather bekommt die Lage (${a1.status})`); const lage = await a1.json(); -const leute = lage.modis || []; +const leute = lage.leute || []; /* SEIT DEM 11.09.2026 GEHOERT DIE RECHTE HAND DAZU. @@ -236,7 +236,7 @@ console.log(""); console.log("--- Beim naechsten Laden ---"); const a2 = await holeLage(keksDogi); -const marina2 = ((await a2.json()).modis || []).find((m) => m.id === idMarina); +const marina2 = ((await a2.json()).leute || []).find((m) => m.id === idMarina); ok(marina2?.stufe === "senior", `die neue Stufe kommt zurueck (${marina2?.stufe})`); const inDb = d.prepare("SELECT stufe FROM personen WHERE id = ?").get(idMarina); ok(inDb?.stufe === "senior", `und steht in der Datenbank (${inDb?.stufe})`); @@ -261,7 +261,7 @@ console.log("--- Gegenprobe: wieder zurueck ---"); const zurueck = await setzeStufe(keksDogi, idMarina, "standard"); ok(zurueck.status === 200, `zurueckstufen geht (${zurueck.status})`); const a3 = await holeLage(keksDogi); -const marina3 = ((await a3.json()).modis || []).find((m) => m.id === idMarina); +const marina3 = ((await a3.json()).leute || []).find((m) => m.id === idMarina); ok(marina3?.stufe === "standard", `und die Anzeige folgt (${marina3?.stufe}) -- sie bleibt also nicht auf "senior" stehen`); diff --git a/server/pruef-werdegang.mjs b/server/pruef-werdegang.mjs index 46409b8d..b897fb5b 100644 --- a/server/pruef-werdegang.mjs +++ b/server/pruef-werdegang.mjs @@ -509,6 +509,40 @@ melde("=== 10. Die Liste ==="); `alphabetisch (${namen.join(", ")})`); ok(/kein vergleich/i.test(l.hinweis || ""), "und die Seite sagt selbst, dass sie niemanden vergleicht"); + + /* DER SATZ UEBER EINER GRUPPE KOMMT VOM SERVER (neu am 21.09.2026). + + Er stand bis dahin als Liste in werdegang.js -- also in einer + Datei, die jeder im Netz ohne Anmeldung herunterladen kann, mit + den Rollennamen als Schluessel. Verschoben wurde er, weil + Rollennamen der Community dort nicht hingehoeren. + + WARUM DAS HIER GEMESSEN WIRD: Beim Verschieben kann man eine + Erklaerung leicht ganz verlieren -- die Oberflaeche zeigt dann + einfach keine Zeile mehr, ohne Fehlermeldung. Eine Gruppe mit + Ueberschrift und ohne Erklaerung sieht nicht kaputt aus, sie sieht + nur aermer aus. Genau solche Verluste faellt niemandem auf. */ + const mitSatz = l.liste.filter((p) => p.rolle_satz && p.rolle_satz.length > 10); + ok(mitSatz.length === l.liste.length, + `jede Karte bringt den Satz ihrer Gruppe mit (${mitSatz.length} von ${l.liste.length})`); + + /* GEGENPROBE: Er darf nicht fuer alle derselbe sein -- sonst haette + das Verschieben aus zwei Saetzen einen gemacht, und die Pruefung + darueber waere trotzdem gruen. */ + const verschieden = new Set(l.liste.map((p) => p.rolle_satz)).size; + ok(verschieden === new Set(l.liste.map((p) => p.rolle)).size, + `und jede Rolle hat ihren eigenen (${verschieden} Saetze auf ` + + `${new Set(l.liste.map((p) => p.rolle)).size} Rollen)`); + + /* UND DIE OBERFLAECHE DARF IHN NICHT MEHR SELBST KENNEN. Bliebe die + alte Liste dort stehen, waere nichts gewonnen -- der Rollenname + stuende weiter in einer ausgelieferten Datei, und beide Fassungen + wuerden auseinanderlaufen. */ + const { readFileSync } = await import("node:fs"); + const vorne = readFileSync( + new URL("../workspace/assets/js/werdegang.js", import.meta.url), "utf8"); + ok(!/GRUPPENSATZ\s*=/.test(vorne), + "und die Oberflaeche fuehrt keine eigene Liste mehr"); } /* ======================================================================= diff --git a/server/workspace-teamlage.js b/server/workspace-teamlage.js index b8073658..7c17040e 100644 --- a/server/workspace-teamlage.js +++ b/server/workspace-teamlage.js @@ -195,7 +195,7 @@ teamlageRouter.get("/workspace/api/teamlage", (req, res) => { /* `eingang` auch hier -- die Oberflaeche darf nicht raten muessen, ob das Feld fehlt oder leer ist. */ return res.json({ - modis: [], eingang: [], angebote: [], bereiche: GESAMT, heute: heuteLokal(), + leute: [], eingang: [], angebote: [], bereiche: GESAMT, heute: heuteLokal(), }); } const platz = modis.map(() => "?").join(","); @@ -378,7 +378,19 @@ teamlageRouter.get("/workspace/api/teamlage", (req, res) => { undefined", weil eine Rolle in der Browser-Liste vergessen worden war. Einmal geschrieben, einmal geaendert. */ stufen: STUFEN.map((wert) => ({ wert, name: STUFEN_NAME[wert] })), - modis: modis.map((m) => { + /* DER SCHLUESSEL HEISST "leute" UND NICHT MEHR NACH DER ROLLE + (21.09.2026). + + Die Oberflaeche liest ihn in teamlage.js -- also in einer + Datei, die jeder im Netz ohne Anmeldung herunterladen kann. + Damit stand der Rollenname der Community dort im CODE, nicht + nur in einem Kommentar. Filipes Regel vom selben Tag: in + Kommentaren ja, in Code und sichtbaren Texten nein. + + Die oertliche Variable darunter heisst weiter `modis` -- sie + verlaesst diese Datei nie. Umbenannt wurde genau das, was + ueber die Leitung geht. */ + leute: modis.map((m) => { const s = stand.get(m.id) || {}; const je = {}; for (const [b, gesamt] of Object.entries(GESAMT)) { diff --git a/server/workspace-werdegang.js b/server/workspace-werdegang.js index 8029c79a..10d5560c 100644 --- a/server/workspace-werdegang.js +++ b/server/workspace-werdegang.js @@ -124,6 +124,22 @@ import { VERBLASST_NACH_TAGEN, tabellen as entwicklungTabellen, } from "./workspace-entwicklung.js"; +/* WAS UEBER EINER GRUPPE STEHT (hierher geholt am 21.09.2026). + + Vorher stand das in werdegang.js, also in einer ausgelieferten + Datei. Hier steht es richtig: Der Server schickt jeder Person ihren + Satz mit, und wer die Rolle nicht sehen darf, bekommt die Person + ohnehin nicht. + + NUR ZWEI EINTRAEGE, UND DAS IST ABSICHT. Manager, Scouts und + Creator stehen im Werdegang unter ihrem Namen, ohne Erklaerung -- + dort ist die Ueberschrift selbsterklaerend. Ein Satz um des Satzes + willen waere Fuellung. */ +const ROLLEN_SATZ = { + hand: "Koordiniert den Alltag und schaut mit dir zusammen auf das Team.", + modi: "Die Leute, die im Chat und im Live stehen.", +}; + export const werdegangRouter = express.Router(); const TAG = 86400000; @@ -626,6 +642,19 @@ werdegangRouter.get("/workspace/api/werdegang/liste", nurLeitung, (req, res) => nächsten Umbenennen auseinanderfällt. */ rolle: p.rolle, rolle_name: ROLLEN_NAME[p.rolle] ?? p.rolle, + /* DER SATZ UNTER DER UEBERSCHRIFT (21.09.2026). + + Er stand bis heute in werdegang.js -- also in einer Datei, + die JEDER herunterlaedt, der den Arbeitsplatz oeffnet. Mit + den Rollennamen als Schluessel, und einer davon gehoert der + Community. Dieselbe Ueberlegung wie bei ROLLENTEXT in + start.js: Was einen Rollennamen traegt, kommt vom Server. + + EIN FEHLENDER SATZ IST EIN GUELTIGER ZUSTAND. Nicht jede + Rolle braucht eine Erklaerung, und ein erfundener Satz waere + schlechter als keiner -- die Oberflaeche laesst die Zeile + dann einfach weg. */ + rolle_satz: ROLLEN_SATZ[p.rolle] ?? null, gesetzt: stand.length, gesamt: ENTWICKLUNG_FREMD.length, bewegt: bewegung.reduce((s, m) => s + m.vor + m.zurueck + m.neu + m.geklaert, 0), diff --git a/workspace/anruf-probe.html b/workspace/anruf-probe.html index 6287c07f..9c98c233 100644 --- a/workspace/anruf-probe.html +++ b/workspace/anruf-probe.html @@ -16,15 +16,15 @@ verdeckt. Gefunden hat es pruef-struktur. Richtig sind `gate.css` (die Farbwerte des Hauses) und `haus.css`. --> - - + + - +