VanVan im Support: „wenn ich … Aufgaben an einen Modi zutrage und
auch bewerte, dann sieht der Modi zwar die Auswertung im Bereich
Entwicklung, aber bei ihm taucht nichts im Bereich eure Aufgaben
unten in der Karte auf."
IHR FALL NACHGESTELLT (server/mess-vanvan-karte.mjs): VanVan traegt
einem Modi zwei von 68 Punkten zu und bewertet sie, Filipe bewertet
nichts -- genau wie bei ihr.
vorher seine Karte: fertig=0 von 68, alles „offen",
Punktzeilen sichtbar: 0
Kennt sie das Wort „zugetragen"? NEIN
nachher Abschnitt „Deine Aufgaben (2)" mit beiden Titeln
ZWEI DINGE WAREN EINS, DIE GETRENNT GEHOEREN
Was er TUN soll die Zuteilung. Die gibt es, bevor jemand etwas
bewertet, und sie gehoert ihm.
Was man SIEHT die Bewertung. Die erscheint erst, wenn alle
hingesehen haben.
`meine-karte` kannte nur das Zweite. War die Bewertung noch nicht
vollstaendig, stieg die Anzeige mit `return` aus -- und damit sah er
gar nichts, obwohl ihm zwei Aufgaben zugetragen waren. Das ist die
falsche Reihenfolge: Wer seine Aufgabe erst sieht, wenn sich zwei
Leute ueber ihre Ausfuehrung einig sind, kann sie nicht angehen.
Die Regel fuer die Bewertung bleibt unveraendert. Eine einzelne
Meinung soll bei einem Menschen nicht als Urteil des Teams ankommen.
UND DIE ANDERE HAELFTE: NIEMAND SAGTE ES IHR
VanVan hatte gesetzt, es kam nicht an, und nichts auf ihrem
Bildschirm erklaerte das. Ein Mensch, der das zweimal erlebt, hoert
auf zu setzen. An einem Punkt, den sie bewertet hat, steht jetzt
leise: „Er sieht das noch nicht -- es fehlt noch eine Einschaetzung."
Ohne Namen: Wer fehlt, waere eine Aufforderung, jemanden
anzutreiben.
IHRE ZWEITE BEOBACHTUNG, EBENFALLS GEMESSEN
„bei jedem Punkt läuft obwohl auch wenn die Aufgaben darüber gar
nicht zugetragen wurde." Stimmt: Die vier Bewertungsknoepfe („✓
Läuft ↗ Wächst ! Da hakt es – Kann ich nicht sagen") stehen an allen
68 Punkten, auch an den nicht zugetragenen. Beim Durchscrollen liest
man deshalb ueberall „Läuft".
DIE 68 BLEIBEN. Filipe am 25.09.2026 ausdruecklich: „dogfather und
die rechte hand sollen immer noch die 68 sachen sehen wie vorher …
unsere sicht soll sich nicht aendern." Also kein Ausblenden und
keine neue Vorgabe, sondern ein Knopf mehr in der Leiste, die es
schon gibt: „Zugetragen · 2". „Alle" bleibt, was beim Aufmachen
gilt. Gemessen grenzt er auf 2 Punkte ein, Kopf „2 / 2".
NEBENBEI ZUSAMMENGEFUEHRT
* `beurteilerZahl()` an einer Stelle -- beide Enden stellen dieselbe
Frage, zwei Abschriften saegten irgendwann Verschiedenes ueber
denselben Punkt.
* `antwortReihe()` und `punkteGefiltert()` ebenso.
* Die Abfrage stand zuerst je Punkt in der Schleife: 68
Datenbankfragen fuer eine Zahl, die sich nicht aendert.
GEPRUEFT
pruef-entwicklung 79/0 (9 neue, mit Gegenproben in beide Richtungen:
ein nicht zugetragener Punkt traegt die Marke nicht, und sobald der
zweite Beurteiler setzt, geht es durch). mess-vanvan-karte ohne
ACHTUNG. pruef-werdegang 103/0, pruef-entwicklung-kacheln 34/0,
pruef-deutsche-texte, pruef-css-klassen, pruef-zwischenspeicher 34/0.
Co-Authored-By: Claude Opus 5 <[email protected]>
655 lines
33 KiB
JavaScript
655 lines
33 KiB
JavaScript
/* ENTWICKLUNG UND TALENTE (11.09.2026)
|
|
|
|
Filipe: "ich will dass ich genau so eine kategorie habe in der
|
|
dogfather und rechten hand rollen, wo wir aufgaben oder bewertungen
|
|
über modis eingeben können. auch für zuschauer die vielleicht modis
|
|
werden könnten wäre auch geil."
|
|
|
|
---------------------------------------------------------------------
|
|
DIE PRÜFUNG, DERENTWEGEN ES DIESE DATEI GIBT
|
|
|
|
In „Entwicklung" steht, was über Menschen aufgeschrieben wird. Wenn
|
|
diese beiden Bereiche irgendwo durchsickern, ist der Schaden nicht
|
|
technisch, sondern menschlich -- und er lässt sich nicht zurücknehmen.
|
|
|
|
Zwei Schlösser sichern das: der ZUGANG (fuehrtTeamDogi) und die
|
|
SICHTBARKEIT der Zeilen. Geprüft werden beide, und zwar von der
|
|
falschen Seite aus: Ein Modi klopft an die Liste, an eine einzelne
|
|
Zeile, ans Ändern und ans Löschen -- und muss überall dieselbe
|
|
Antwort bekommen wie jemand, für den es diese Bereiche nicht gibt.
|
|
|
|
Dazu die Brücke: Ein Eintrag lässt sich EINMAL an die Person
|
|
schicken. Gemessen wird, dass die Nachricht wirklich in ihrem Chat
|
|
ankommt (sonst wäre „geschickt" nur ein Datum) und dass ein zweiter
|
|
Versuch abgelehnt wird.
|
|
===================================================================== */
|
|
|
|
import { mkdtempSync, rmSync } from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
import { join } from "node:path";
|
|
import { request } from "node:http";
|
|
|
|
const ordner = mkdtempSync(join(tmpdir(), "ws-entw-"));
|
|
process.env.WORKSPACE_DB = join(ordner, "workspace.db");
|
|
|
|
const { eigenerPort } = await import("./helfer-port.mjs");
|
|
const PORT = await eigenerPort(import.meta, "die Entwicklungspruefung");
|
|
|
|
process.env.PORT = `${PORT}`;
|
|
process.env.SITE_ACCESS_SECRET = "lokaler-test";
|
|
process.env.SITE_PUBLIC_LAUNCH_AT = "2020-01-01T00:00:00+01:00";
|
|
|
|
const express = (await import("express")).default;
|
|
const ec = express.response.cookie;
|
|
express.response.cookie = function (n, w, o) { return ec.call(this, n, w, { ...(o || {}), secure: false }); };
|
|
await import("./index.js");
|
|
await new Promise((r) => setTimeout(r, 1000));
|
|
|
|
for (const art of ["uncaughtException", "unhandledRejection"]) {
|
|
process.on(art, (f) => { console.error(""); console.error("ABBRUCH:", f?.stack || f); process.exit(1); });
|
|
}
|
|
setTimeout(() => { console.error(""); console.error("ABBRUCH: 150 s vorbei."); process.exit(1); },
|
|
150000).unref?.();
|
|
|
|
let fehler = 0, geprueft = 0;
|
|
const ok = (b, t) => { geprueft++; console.log((b ? " ok " : " FEHL ") + t); if (!b) fehler++; };
|
|
|
|
const CREW = "crew.dogfather-universe.com";
|
|
const WS = "workspace.dogfather-universe.com";
|
|
|
|
function hole(pfad, { host = WS, methode = "GET", rumpf = null, keks = "" } = {}) {
|
|
return new Promise((fertig, schief) => {
|
|
const kopf = { Host: host };
|
|
if (rumpf) { kopf["Content-Type"] = "application/json"; kopf["Content-Length"] = Buffer.byteLength(rumpf); }
|
|
if (keks) kopf.Cookie = keks;
|
|
const a = request({ host: "127.0.0.1", port: PORT, path: pfad, method: methode, headers: kopf },
|
|
(antwort) => {
|
|
let text = "";
|
|
antwort.setEncoding("utf8");
|
|
antwort.on("data", (t) => { text += t; });
|
|
antwort.on("end", () => {
|
|
let daten = null;
|
|
try { daten = JSON.parse(text); } catch { /* kein JSON */ }
|
|
fertig({ status: antwort.statusCode, text, daten,
|
|
keks: (antwort.headers["set-cookie"] || []).map((z) => z.split(";")[0]).join("; ") });
|
|
});
|
|
});
|
|
a.on("error", schief);
|
|
a.setTimeout(15000, () => { a.destroy(new Error("Zeitueberschreitung")); });
|
|
if (rumpf) a.write(rumpf);
|
|
a.end();
|
|
});
|
|
}
|
|
|
|
const { DatabaseSync } = await import("node:sqlite");
|
|
const { scryptSync, randomBytes } = await import("node:crypto");
|
|
await hole("/workspace/api/anmelden", { methode: "POST",
|
|
rumpf: JSON.stringify({ rolle: "admin", code: "x" }) }).catch(() => {});
|
|
for (let i = 0; i < 60; i++) {
|
|
try {
|
|
const p = new DatabaseSync(process.env.WORKSPACE_DB);
|
|
try { p.prepare("SELECT 1 FROM personen LIMIT 1").get(); break; } finally { p.close(); }
|
|
} catch { await new Promise((r) => setTimeout(r, 250)); }
|
|
}
|
|
const d = new DatabaseSync(process.env.WORKSPACE_DB);
|
|
const jetzt = new Date().toISOString();
|
|
function anlegen(name, rolle, code) {
|
|
const salt = randomBytes(16).toString("hex");
|
|
const hash = scryptSync(code, salt, 64, { N: 32768, r: 8, p: 1, maxmem: 96 * 1024 * 1024 }).toString("hex");
|
|
d.prepare("INSERT INTO personen (name, rolle, code_hash, code_salt, code_n, aktiv, erstellt) VALUES (?,?,?,?,?,1,?)")
|
|
.run(name, rolle, hash, salt, 32768, jetzt);
|
|
return d.prepare("SELECT last_insert_rowid() AS id").get().id;
|
|
}
|
|
const idDogi = anlegen("Filipe", "admin", "CODE-DOGI-0001");
|
|
anlegen("VanVan", "hand", "CODE-HAND-0001");
|
|
const idModi = anlegen("Ayla", "modi", "CODE-TEAM-0001");
|
|
anlegen("Cigdem", "manager", "CODE-MANA-0001");
|
|
|
|
async function anmelden(rolle, code, wand) {
|
|
const a = await hole("/workspace/api/anmelden", { host: wand, methode: "POST",
|
|
rumpf: JSON.stringify({ rolle, code }) });
|
|
return { ok: a.status === 200, keks: a.keks };
|
|
}
|
|
const dogi = await anmelden("admin", "CODE-DOGI-0001", CREW);
|
|
const hand = await anmelden("hand", "CODE-HAND-0001", CREW);
|
|
const ayla = await anmelden("modi", "CODE-TEAM-0001", CREW);
|
|
const mana = await anmelden("manager", "CODE-MANA-0001", WS);
|
|
ok(dogi.ok && hand.ok && ayla.ok && mana.ok, "alle vier sind angemeldet");
|
|
|
|
const ENTW = "/workspace/api/bereich/entwicklung";
|
|
const TAL = "/workspace/api/bereich/talente";
|
|
|
|
/* =======================================================================
|
|
1. Es gibt die beiden Bereiche -- und ihre Felder
|
|
======================================================================= */
|
|
console.log("");
|
|
console.log("=== Die beiden Bereiche ===");
|
|
{
|
|
const a = await hole(ENTW, { host: CREW, keks: dogi.keks });
|
|
ok(a.daten?.einstellung?.name === "Entwicklung",
|
|
`DogFather kann Entwicklung oeffnen (${a.daten?.einstellung?.name})`);
|
|
const arten = Object.keys(a.daten?.einstellung?.arten || {});
|
|
ok(arten.length === 5, `fuenf Arten (${arten.join(", ")})`);
|
|
/* DIE REIHENFOLGE IST ABSICHT: Wer ein Formular oeffnet, dessen
|
|
erstes Feld "Problem" heisst, schreibt Probleme auf. */
|
|
ok(arten[0] === "staerke" && arten[1] === "dank",
|
|
"und die ersten beiden sind die guten");
|
|
ok(arten.includes("last"),
|
|
"„Zu viel gerade“ ist dabei -- der haeufigste Grund aufzuhoeren");
|
|
|
|
/* DAS NOTIZBRETT MUSS ERREICHBAR BLEIBEN -- gerade WEIL die Kachel
|
|
nicht mehr darauf zeigt.
|
|
|
|
Genau hier ist am 11.09.2026 etwas kaputtgegangen: Die Liste der
|
|
Bretter, die ein Modi oder die rechte Hand oeffnen darf, wird aus
|
|
den KACHELZIELEN abgeleitet. Als die Kacheln auf die Katalogseiten
|
|
umgestellt wurden, fielen beide Bretter aus der Liste -- und die
|
|
Seite verlinkte auf ein 404. Kein Fehler im Riegel, sondern in
|
|
seiner Quelle.
|
|
|
|
Diese Zeile ist die einzige, die das gemerkt hat. */
|
|
const b = await hole(TAL, { host: CREW, keks: hand.keks });
|
|
ok(b.daten?.einstellung?.name === "Talente",
|
|
`die rechte Hand kann Talente oeffnen (${b.daten?.einstellung?.name})`);
|
|
const bE = await hole(ENTW, { host: CREW, keks: hand.keks });
|
|
ok(bE.daten?.einstellung?.name === "Entwicklung",
|
|
`und das Notizbrett Entwicklung auch (${bE.daten?.einstellung?.name})`);
|
|
const tArten = Object.keys(b.daten?.einstellung?.arten || {});
|
|
ok(tArten.includes("empfohlen"),
|
|
`die Empfehlung aus dem Team ist dabei (${tArten.join(", ")})`);
|
|
}
|
|
|
|
/* =======================================================================
|
|
2. DIE EIGENTLICHE PRUEFUNG: Wer NICHT hineinkommt
|
|
======================================================================= */
|
|
console.log("");
|
|
console.log("=== Wer nicht hineinkommt ===");
|
|
let eintragId = 0;
|
|
{
|
|
/* Erst ein Eintrag, damit es ueberhaupt etwas zu verbergen gibt --
|
|
sonst waere jede Sperre unten gruen ueber einer leeren Liste. */
|
|
const a = await hole(ENTW, { host: CREW, methode: "POST", keks: dogi.keks,
|
|
rumpf: JSON.stringify({
|
|
art: "staerke", titel: "Ruhig geblieben, als es kippte",
|
|
text: "Hat den Streit im Chat gedreht, ohne jemanden bloßzustellen.",
|
|
datum: "2026-09-11", creator_id: idModi, einsatz: "Beim nächsten Live wieder ihr überlassen",
|
|
}) });
|
|
eintragId = a.daten?.id;
|
|
ok(a.status === 201 && eintragId > 0,
|
|
`ein Eintrag ueber Ayla (${a.status}) ${a.status === 201 ? "" : a.text.slice(0, 160)}`);
|
|
|
|
for (const [wer, keks, wand] of [["ein Teammitglied", ayla.keks, CREW],
|
|
["ein Manager", mana.keks, WS]]) {
|
|
const liste = await hole(ENTW, { host: wand, keks });
|
|
ok(liste.status === 404, `${wer}: die Liste gibt es nicht (${liste.status})`);
|
|
const aendern = await hole(`${ENTW}/${eintragId}`, { host: wand, methode: "PATCH",
|
|
keks, rumpf: JSON.stringify({ titel: "umgeschrieben" }) });
|
|
ok(aendern.status === 404, `${wer}: aendern geht nicht (${aendern.status})`);
|
|
const weg = await hole(`${ENTW}/${eintragId}`, { host: wand, methode: "DELETE", keks });
|
|
ok(weg.status === 404, `${wer}: loeschen auch nicht (${weg.status})`);
|
|
const tal = await hole(TAL, { host: wand, keks });
|
|
ok(tal.status === 404, `${wer}: Talente ebenfalls nicht (${tal.status})`);
|
|
}
|
|
|
|
/* Und die Zeile steht unveraendert da -- sonst waeren die 404 oben
|
|
Antworten auf Anfragen, die trotzdem gewirkt haben. */
|
|
const heil = d.prepare("SELECT titel FROM eintraege WHERE id = ?").get(eintragId);
|
|
ok(heil?.titel === "Ruhig geblieben, als es kippte", "der Eintrag ist unveraendert");
|
|
|
|
/* GEGENPROBE: Die beiden, die duerfen, sehen ihn sehr wohl. Ohne sie
|
|
hiesse "niemand sieht ihn" auch dann gruen, wenn er gar nicht
|
|
angekommen waere. */
|
|
for (const [wer, keks] of [["DogFather", dogi.keks], ["die rechte Hand", hand.keks]]) {
|
|
const a = await hole(ENTW, { host: CREW, keks });
|
|
ok((a.daten?.eintraege || []).some((e) => e.id === eintragId),
|
|
`${wer} sieht ihn`);
|
|
}
|
|
}
|
|
|
|
/* =======================================================================
|
|
3. Die Bruecke: einmal an die Person schicken
|
|
======================================================================= */
|
|
console.log("");
|
|
console.log("=== An die Person schicken ===");
|
|
{
|
|
const a = await hole(`${ENTW}/${eintragId}/senden`, { host: CREW, methode: "POST",
|
|
keks: dogi.keks, rumpf: "{}" });
|
|
ok(a.status === 200, `geschickt (${a.status})`);
|
|
|
|
/* KOMMT SIE WIRKLICH AN? Ohne diese Zeile waere "geschickt" nur ein
|
|
Datum in einer Spalte. Nachgesehen wird in AYLAS Chat, nicht in
|
|
der Antwort der Route. */
|
|
const raeume = await hole("/workspace/api/chat/raeume", { host: CREW, keks: ayla.keks });
|
|
const raum = (raeume.daten?.raeume || [])[0];
|
|
ok(!!raum, `Ayla hat ein Gespraech (${(raeume.daten?.raeume || []).length})`);
|
|
const verlauf = await hole(`/workspace/api/chat/raeume/${raum?.id}/nachrichten`,
|
|
{ host: CREW, keks: ayla.keks });
|
|
const texte = (verlauf.daten?.nachrichten || []).map((n) => n.text || "").join(" ");
|
|
ok(/Ruhig geblieben, als es kippte/.test(texte), "und die Nachricht steht darin");
|
|
ok(/Das läuft gut/.test(texte),
|
|
"mit der Art davor -- sonst waere es ein Fetzen ohne Zusammenhang");
|
|
ok(/Beim nächsten Live wieder ihr überlassen/.test(texte),
|
|
"und dem, was daraus folgen soll");
|
|
|
|
/* EINMAL IST EINMAL. Zweimal dasselbe Lob ist kein doppeltes Lob. */
|
|
const b = await hole(`${ENTW}/${eintragId}/senden`, { host: CREW, methode: "POST",
|
|
keks: dogi.keks, rumpf: "{}" });
|
|
ok(b.status === 409, `ein zweites Mal wird abgelehnt (${b.status})`);
|
|
const nachher = await hole(`/workspace/api/chat/raeume/${raum?.id}/nachrichten`,
|
|
{ host: CREW, keks: ayla.keks });
|
|
ok((nachher.daten?.nachrichten || []).length === (verlauf.daten?.nachrichten || []).length,
|
|
"und es kam keine zweite Nachricht an");
|
|
|
|
/* Ein Eintrag ohne Person laesst sich nicht schicken -- an wen auch. */
|
|
const ohne = await hole(ENTW, { host: CREW, methode: "POST", keks: dogi.keks,
|
|
rumpf: JSON.stringify({ art: "wachsen", titel: "Allgemeine Notiz", datum: "2026-09-11" }) });
|
|
const c = await hole(`${ENTW}/${ohne.daten?.id}/senden`, { host: CREW, methode: "POST",
|
|
keks: dogi.keks, rumpf: "{}" });
|
|
ok(c.status === 400, `ein Eintrag ohne Person geht nicht raus (${c.status})`);
|
|
}
|
|
|
|
/* =======================================================================
|
|
4. Die Kacheln
|
|
======================================================================= */
|
|
console.log("");
|
|
console.log("=== Die Kacheln ===");
|
|
{
|
|
/* DIE KACHELN ZEIGEN SEIT DEM 11.09.2026 AUF DIE KATALOGSEITEN.
|
|
|
|
Vorher stand hier `b=(entwicklung|talente)` -- das Ziel war ein
|
|
Brett. Filipe: "ich will dass fertige aufgaben da stehen und so".
|
|
Seither fuehrt die Kachel auf entwicklung.html bzw. talente.html;
|
|
das Notizbrett gibt es weiter und ist von dort verlinkt.
|
|
|
|
GEPRUEFT WIRD DER NAME DER SEITE UND NICHT MEHR DAS BRETT -- eine
|
|
Kachel, die auf ein Brett zeigt, waere ab jetzt der Rueckschritt,
|
|
den diese Zeile melden soll. */
|
|
for (const [wer, keks] of [["DogFather", dogi.keks], ["die rechte Hand", hand.keks]]) {
|
|
const a = await hole("/workspace/api/ich", { host: CREW, keks });
|
|
const alle = [...(a.daten?.bereiche || []), ...(a.daten?.bereiche_zusatz || [])];
|
|
/* NACH DEM NAMEN UND NICHT NACH DEM ZIEL.
|
|
|
|
Seit dem 11.09.2026 zeigen DREI Kacheln auf entwicklung.html: die
|
|
beiden Katalogkacheln und „Wie geht's dir?" -- dieselbe Seite,
|
|
drei verschiedene Zwecke, weil der Server entscheidet, was ein
|
|
Modi dort sieht. Ein Filter nach dem Ziel zaehlt sie alle und
|
|
meldet einen Fehler, wo keiner ist.
|
|
|
|
Der Name ist hier die ehrlichere Frage: Es geht um DIESE zwei
|
|
Kacheln, nicht um alles, was zufaellig dieselbe Adresse hat. */
|
|
/* UMBENANNT AM 19.09.2026: aus "Entwicklung" wurde "Eure Aufgaben"
|
|
(die Kachel fuehrt auf den Aufgabenkatalog). Der Name steht hier
|
|
weiterhin, weil der Kommentar darueber ihn ausdruecklich als die
|
|
ehrlichere Frage bezeichnet -- nur eben der neue. */
|
|
const beide = alle.filter((k) => ["Eure Aufgaben", "Talente"].includes(k.name));
|
|
ok(beide.length === 2, `${wer}: beide Kacheln (${beide.map((k) => k.name).join(", ") || "keine"})`);
|
|
ok(beide.every((k) => k.gruppe === "Entwicklung & Nachwuchs"),
|
|
`${wer}: in einer eigenen Gruppe (${[...new Set(beide.map((k) => k.gruppe))].join(", ")})`);
|
|
}
|
|
/* DIE DRITTE KACHEL -- und sie gehoert ausdruecklich ALLEN im Team.
|
|
|
|
„Wie geht's dir?" ist die einzige Kachel des Hauses, deren Inhalt
|
|
niemand ausser der Person selbst je sieht. Sie steht auch bei
|
|
DogFather und der rechten Hand: Wenn es jemandem zu viel wird,
|
|
dann ihnen genauso. Eine Kachel, die nur die anderen ausfuellen,
|
|
waere genau die Schieflage, die dieses Haus nicht haben soll. */
|
|
for (const [wer, keks] of [["DogFather", dogi.keks], ["die rechte Hand", hand.keks],
|
|
["ein Modi", ayla.keks]]) {
|
|
const x = await hole("/workspace/api/ich", { host: CREW, keks });
|
|
const alleX = [...(x.daten?.bereiche || []), ...(x.daten?.bereiche_zusatz || [])];
|
|
/* SEIT DEM 15.09.2026 HAT SIE EINE EIGENE SEITE.
|
|
|
|
Bis dahin zeigte sie auf entwicklung.html -- und damit auf Namen
|
|
und Beobachtungen ueber ANDERE, waehrend auf der Kachel steht
|
|
"die Antworten sieht nur du". Filipe hat es gemeldet, und die
|
|
Zeile hier hat den Fehler mitgetragen, weil sie genau danach
|
|
gesucht hat. */
|
|
const befinden = alleX.find((k) => k.ziel === "befinden.html");
|
|
ok(!!befinden, `${wer} hat „${befinden?.name || "—"}"`);
|
|
ok(befinden?.gruppe === "Für dich",
|
|
`und sie steht bei „Für dich" (${befinden?.gruppe})`);
|
|
/* GEGENPROBE: Die Kachel, die VERTRAULICHKEIT VERSPRICHT, fuehrt
|
|
nicht dorthin, wo andere Menschen stehen.
|
|
|
|
HIER STAND EINE NAMENSLISTE (20.09.2026 ersetzt). Sie lautete
|
|
`!["Eure Aufgaben", "Talente"].includes(k.name)` -- und war damit
|
|
eine abgeschriebene Liste, genau die Sorte, vor der dieses Haus
|
|
an drei Stellen warnt. Als ein Modi eine eigene Kachel bekam
|
|
("Deine Aufgaben", weil "Eure" aus seiner Sicht falsch ist),
|
|
wurde diese Pruefung rot, OHNE dass irgendetwas kaputt war. Eine
|
|
rote Pruefung, die rot bleibt, gewoehnt einem das Hinsehen ab --
|
|
sie ist schaedlicher als gar keine.
|
|
|
|
Geprueft wird jetzt, was Filipe eigentlich gemeldet hat: Auf der
|
|
Kachel stand "die Antworten sieht nur du", und sie fuehrte auf
|
|
eine Seite mit Namen anderer Leute. Das ist eine Eigenschaft des
|
|
VERSPRECHENS, nicht des Namens -- und Versprechen stehen im
|
|
Untertitel. Wer morgen eine Kachel "Dein Kopf" nennt und dasselbe
|
|
verspricht, ist damit mitgeprueft; die Namensliste haette ihn
|
|
durchgelassen. */
|
|
const verspricht = (k) => /\bnur du\b|\bnur dich\b|siehst nur du/i.test(k.unter || "");
|
|
const heikel = alleX.filter((k) => verspricht(k) && k.ziel === "entwicklung.html");
|
|
ok(alleX.some(verspricht) && heikel.length === 0,
|
|
`${wer}: keine Kachel verspricht Vertraulichkeit und fuehrt doch auf die`
|
|
+ ` Entwicklungsseite (${alleX.filter(verspricht).length} mit Versprechen geprueft)`);
|
|
|
|
/* UND DIE ECHTE GRENZE, die bisher NIEMAND gemessen hat.
|
|
Ob eine Kachel richtig zeigt, ist Beschriftung. Ob die Seite
|
|
dahinter fremde Namen herausgibt, sind Daten -- und nur das
|
|
schuetzt jemanden wirklich. Ein Modi darf dort NIEMANDEN
|
|
bekommen ausser sich selbst. */
|
|
const lage = await hole("/workspace/api/entwicklung/lage", { host: CREW, keks });
|
|
const fremde = (lage.daten?.personen || []).filter((q) => q.id !== undefined);
|
|
if (wer === "ein Modi") {
|
|
ok(lage.status === 200 && fremde.length === 0,
|
|
`${wer} bekommt auf der Entwicklungsseite KEINE fremden Namen (${fremde.length})`);
|
|
} else {
|
|
/* Die Gegenprobe zur Zeile darueber: Waere der Filter kaputt und
|
|
liesse niemanden durch, saehe auch die Leitung nichts -- und
|
|
die Modi-Zeile waere trotzdem gruen. Beide Richtungen gehoeren
|
|
gemessen, sonst beweist keine etwas. */
|
|
ok(lage.status === 200 && fremde.length > 0,
|
|
`${wer} bekommt sie sehr wohl (${fremde.length}) -- der Filter ist nicht einfach zu`);
|
|
}
|
|
}
|
|
|
|
const a = await hole("/workspace/api/ich", { host: CREW, keks: ayla.keks });
|
|
const alle = [...(a.daten?.bereiche || []), ...(a.daten?.bereiche_zusatz || [])];
|
|
/* UMGEDREHT AM 22.09.2026 -- fuer "Eure Aufgaben", nicht fuer
|
|
"Talente".
|
|
---------------------------------------------------------------------
|
|
Hier stand, ein Modi bekomme BEIDE Kacheln nicht angeboten. Filipe
|
|
zum Bildschirmfoto der Gruppe "Entwicklung & Nachwuchs": "die modis
|
|
sollen diesen bereich und diese zwei kacheln auch sehen, die sollen
|
|
aber nur ihre aufgaben und daten sehen."
|
|
|
|
DASS ER SIE SIEHT, IST KEIN WIDERSPRUCH ZUR ZEILE DARUEBER: Die
|
|
misst, dass auf der Seite keine FREMDEN Namen stehen -- und das
|
|
gilt unveraendert. Die Kachel oeffnet die Seite; was darauf steht,
|
|
entscheidet der Server.
|
|
|
|
"TALENTE" BLEIBT DRAUSSEN. Dort stehen Notizen ueber Zuschauer, die
|
|
nichts davon wissen. */
|
|
ok(alle.some((k) => k.name === "Eure Aufgaben"),
|
|
`ein Teammitglied bekommt "Eure Aufgaben" angeboten (${alle.length} Kacheln)`);
|
|
ok(alle.some((k) => k.name === "Entwicklung"),
|
|
`und "Entwicklung" ebenso`);
|
|
ok(!alle.some((k) => k.name === "Talente"),
|
|
`"Talente" aber nicht -- dort stehen Notizen ueber Fremde`);
|
|
|
|
/* Kein Farbton doppelt -- zwei Kacheln in derselben Farbe sind ein
|
|
Fehler, den man sonst erst am Bildschirm findet. */
|
|
const dogiIch = await hole("/workspace/api/ich", { host: CREW, keks: dogi.keks });
|
|
const toene = [...(dogiIch.daten?.bereiche || [])].map((k) => k.ton).filter((t) => t !== undefined);
|
|
ok(new Set(toene).size === toene.length, `kein Farbton doppelt (${toene.length} Kacheln)`);
|
|
}
|
|
|
|
/* =====================================================================
|
|
WELCHE PUNKTE GELTEN FUER DIESEN MENSCHEN (25.09.2026)
|
|
=====================================================================
|
|
|
|
Filipe: „ich will dass die rechte hand und ich diese aufgaben die es
|
|
da gibt, individuel aussuchen koennen wer welche aufgabe bekommt.
|
|
bis dahin sollen die keine aufgaben sehen. nur die, die die rechte
|
|
hand oder dogfather ihnen zutragen."
|
|
|
|
BIS HEUTE GALTEN ALLE 68 FUER JEDEN. Geprueft wird deshalb zuerst
|
|
das Wichtigste an seinem Satz -- „bis dahin sollen die keine
|
|
aufgaben sehen": Ohne Zuteilung ist die Karte LEER. Ohne diese
|
|
Zeile waere alles Folgende auch dann gruen, wenn weiterhin alles
|
|
fuer jeden gilt.
|
|
===================================================================== */
|
|
console.log("\n=== Punkte je Person aussuchen ===");
|
|
{
|
|
const karte = async (keks) => (await hole(
|
|
`/workspace/api/entwicklung/person/${idModi}`, { host: CREW, keks })).daten;
|
|
|
|
const vorher = await karte(dogi.keks);
|
|
const allePunkte = (b) => (b?.bloecke || []).flatMap((x) => x.punkte);
|
|
/* DIE SICHT DER LEITUNG AENDERT SICH NICHT (berichtigt 25.09.2026).
|
|
|
|
Der erste Anlauf hat die Karte auf das Zugetragene eingegrenzt.
|
|
Filipe: „falsch, dogfather und die rechte hand sollen immer noch
|
|
die 68 sachen sehen wie vorher … aber unsere sicht von dogfather
|
|
und rechte hand soll sich nicht aendern."
|
|
|
|
Die Pruefung haelt das jetzt ausdruecklich fest -- sonst waere die
|
|
naechste Eingrenzung wieder eine Zeile Arbeit und niemandem
|
|
aufgefallen. */
|
|
ok(allePunkte(vorher).length === vorher?.gesamt_katalog
|
|
&& (vorher?.gesamt_katalog || 0) >= 60,
|
|
`die Leitung sieht den ganzen Katalog (${allePunkte(vorher).length} Punkte)`);
|
|
ok(vorher && vorher.gesamt === 0,
|
|
`ohne Zutragung zaehlt die Karte trotzdem nichts (${vorher?.gesamt})`);
|
|
ok(allePunkte(vorher).every((pt) => pt.zugetragen === false),
|
|
`und kein Punkt ist als Aufgabe markiert `
|
|
+ `(${allePunkte(vorher).filter((pt) => pt.zugetragen).length})`);
|
|
|
|
/* DIE LISTE ZUM AUSSUCHEN zeigt ALLES, mit einem Haken an dem, was
|
|
gilt. Zeigte sie nur das Zugeteilte, koennte man nie etwas
|
|
hinzufuegen -- ein Auswahlfenster, in dem die Auswahl fehlt. */
|
|
const wahl = (await hole(`/workspace/api/entwicklung/person/${idModi}/punkte`,
|
|
{ host: CREW, keks: dogi.keks })).daten;
|
|
const alle = (wahl?.bloecke || []).flatMap((b) => b.punkte);
|
|
ok(alle.length === vorher?.gesamt_katalog,
|
|
`die Auswahl zeigt den ganzen Katalog (${alle.length})`);
|
|
ok(alle.every((x) => x.dabei === false),
|
|
`und nichts davon ist angehakt (${alle.filter((x) => x.dabei).length} angehakt)`);
|
|
|
|
/* ---- Zwoelf aussuchen ------------------------------------------- */
|
|
const zwoelf = alle.slice(0, 12).map((x) => x.schluessel);
|
|
const gesetzt = await hole(`/workspace/api/entwicklung/person/${idModi}/punkte`,
|
|
{ host: CREW, keks: dogi.keks, methode: "PUT",
|
|
rumpf: JSON.stringify({ punkte: zwoelf }) });
|
|
ok(gesetzt.status === 200 && gesetzt.daten?.zugeteilt === 12,
|
|
`DogFather sucht zwoelf aus (HTTP ${gesetzt.status}, ${gesetzt.daten?.zugeteilt})`);
|
|
|
|
const nachher = await karte(dogi.keks);
|
|
const markiert = allePunkte(nachher).filter((pt) => pt.zugetragen).map((x) => x.schluessel);
|
|
/* DIE LISTE BLEIBT VOLL, die Marke wandert. Das ist der ganze
|
|
Unterschied zwischen „eingrenzen" und „zutragen". */
|
|
ok(allePunkte(nachher).length === vorher?.gesamt_katalog,
|
|
`die Liste bleibt vollstaendig (${allePunkte(nachher).length})`);
|
|
ok(nachher?.gesamt === 12 && markiert.length === 12,
|
|
`und genau zwoelf sind als Aufgabe markiert (${markiert.length})`);
|
|
ok(zwoelf.every((s) => markiert.includes(s)),
|
|
" und zwar dieselben, nicht irgendwelche zwoelf");
|
|
|
|
/* DIE RECHTE HAND SIEHT DASSELBE -- sie beurteilt denselben
|
|
Menschen, und zwei verschiedene Listen fuer dieselbe Person waeren
|
|
der Grund fuer jede Abweichung, die keine ist. */
|
|
const beiHand = await karte(hand.keks);
|
|
ok(beiHand?.gesamt === 12,
|
|
`die rechte Hand sieht dieselben zwoelf (${beiHand?.gesamt})`);
|
|
|
|
/* UND SIE DARF SELBST AUSSUCHEN -- Filipes Satz nennt beide. */
|
|
const vonHand = await hole(`/workspace/api/entwicklung/person/${idModi}/punkte`,
|
|
{ host: CREW, keks: hand.keks, methode: "PUT",
|
|
rumpf: JSON.stringify({ punkte: alle.slice(0, 5).map((x) => x.schluessel) }) });
|
|
ok(vonHand.status === 200 && vonHand.daten?.zugeteilt === 5,
|
|
`die rechte Hand sucht ebenfalls aus (HTTP ${vonHand.status}, ${vonHand.daten?.zugeteilt})`);
|
|
|
|
/* ---- 4b. EINZELN ZUTRAGEN, DIREKT AM PUNKT (25.09.2026) --------
|
|
|
|
Filipe: „ich will sofort über diese aufgaben da spezifisch
|
|
zuteilen kann bitte. bei allen personnen."
|
|
|
|
Das Auswahlfenster setzt die GANZE Liste; dieser Weg setzt EINEN
|
|
Punkt. Der Unterschied ist nicht nur Bequemlichkeit: Wuerde der
|
|
Knopf am Punkt die ganze Liste schicken, loeschte er die
|
|
Zutragung, die jemand anderes eine Sekunde vorher gemacht hat.
|
|
Genau das wird hier gemessen -- erst zutragen, dann pruefen, dass
|
|
die vorherigen unberuehrt sind. */
|
|
{
|
|
const einer = alle[20].schluessel;
|
|
const vorZahl = (await karte(dogi.keks))?.gesamt || 0;
|
|
const weg = (an, s = einer, keks = dogi.keks) =>
|
|
hole(`/workspace/api/entwicklung/person/${idModi}/punkt/${s}`,
|
|
{ host: CREW, keks, methode: "PUT", rumpf: JSON.stringify({ an }) });
|
|
|
|
const a1 = await weg(true);
|
|
ok(a1.status === 200 && a1.daten?.zugetragen === true,
|
|
`ein einzelner Punkt laesst sich direkt am Punkt zutragen (HTTP ${a1.status})`);
|
|
ok(a1.daten?.gesamt === vorZahl + 1,
|
|
`und die neue Zahl kommt vom Server zurueck (${vorZahl} -> ${a1.daten?.gesamt})`);
|
|
|
|
const k1 = await karte(dogi.keks);
|
|
const markiert1 = (k1?.bloecke || []).flatMap((b) => b.punkte)
|
|
.filter((pt) => pt.zugetragen).map((pt) => pt.schluessel);
|
|
ok(markiert1.includes(einer) && markiert1.length === vorZahl + 1,
|
|
`die vorher zugetragenen bleiben unberuehrt (${markiert1.length})`);
|
|
|
|
/* ZWEIMAL DASSELBE IST KEIN FEHLER. Wer zweimal tippt oder zwei
|
|
Fenster offen hat, bekommt denselben Zustand -- nicht eine
|
|
Absage und nicht einen doppelten Eintrag. */
|
|
const a2 = await weg(true);
|
|
ok(a2.status === 200 && a2.daten?.gesamt === a1.daten?.gesamt,
|
|
`zweimal zutragen aendert nichts (${a2.daten?.gesamt})`);
|
|
|
|
const a3 = await weg(false);
|
|
ok(a3.status === 200 && a3.daten?.gesamt === vorZahl,
|
|
`und einzeln wieder wegnehmen ebenso (${a3.daten?.gesamt})`);
|
|
|
|
const quatsch = await weg(true, "gibt_es_nicht");
|
|
ok(quatsch.status === 400,
|
|
`Gegenprobe: ein erfundener Punkt wird abgewiesen (${quatsch.status})`);
|
|
const vonModi2 = await weg(true, einer, ayla.keks);
|
|
ok(vonModi2.status >= 400,
|
|
`Gegenprobe: ein Modi traegt nichts zu (${vonModi2.status})`);
|
|
}
|
|
|
|
/* ---- Gegenproben ------------------------------------------------- */
|
|
/* EIN ERFUNDENER SCHLUESSEL WIRD VERWORFEN. Ohne diesen Abgleich
|
|
liesse sich beliebiger Text in die Tabelle schreiben -- er stuende
|
|
danach in keiner Liste und waere auch nicht wegzubekommen. */
|
|
const mitMuell = await hole(`/workspace/api/entwicklung/person/${idModi}/punkte`,
|
|
{ host: CREW, keks: dogi.keks, methode: "PUT",
|
|
rumpf: JSON.stringify({ punkte: [...zwoelf, "gibt_es_nicht", "auch_nicht"] }) });
|
|
ok(mitMuell.daten?.zugeteilt === 12,
|
|
`Gegenprobe: erfundene Schluessel fallen weg (${mitMuell.daten?.zugeteilt} statt 14)`);
|
|
|
|
/* EIN MODI DARF NICHT AUSSUCHEN -- weder fuer sich noch fuer
|
|
andere. Ohne diese Zeile hiesse „DogFather darf" nur, dass
|
|
ueberhaupt jemand darf. */
|
|
const vonModi = await hole(`/workspace/api/entwicklung/person/${idModi}/punkte`,
|
|
{ host: CREW, keks: ayla.keks, methode: "PUT",
|
|
rumpf: JSON.stringify({ punkte: [] }) });
|
|
ok(vonModi.status >= 400,
|
|
`Gegenprobe: ein Modi sucht nicht aus (${vonModi.status})`);
|
|
const nochDa = await karte(dogi.keks);
|
|
ok(nochDa?.gesamt === 12,
|
|
` und nach dem Versuch stehen die zwoelf noch (${nochDa?.gesamt})`);
|
|
|
|
/* ALLES WEGNEHMEN GEHT AUCH. Eine Auswahl, die man nur erweitern
|
|
kann, waere eine Falle: Wer sich vertut, bekaeme den Punkt nie
|
|
wieder los. */
|
|
const leer = await hole(`/workspace/api/entwicklung/person/${idModi}/punkte`,
|
|
{ host: CREW, keks: dogi.keks, methode: "PUT",
|
|
rumpf: JSON.stringify({ punkte: [] }) });
|
|
const danach = await karte(dogi.keks);
|
|
ok(leer.status === 200 && danach?.gesamt === 0,
|
|
`und alles wieder wegnehmen geht ebenfalls (${danach?.gesamt})`);
|
|
}
|
|
|
|
/* =======================================================================
|
|
VANVANS MELDUNG AUS DEM SUPPORT (30.09.2026)
|
|
|
|
„Wenn ich … Aufgaben an einen Modi zutrage und auch bewerte, dann
|
|
sieht der Modi zwar die Auswertung im Bereich Entwicklung, aber bei
|
|
ihm taucht nichts im Bereich eure Aufgaben unten in der Karte auf."
|
|
|
|
Nachgestellt im Browser (mess-vanvan-karte) war es genau so: Seine
|
|
Karte lieferte alle 68 Punkte des Katalogs und kein Wort darueber,
|
|
welche davon ihm zugetragen wurden -- und weil noch nicht alle
|
|
Beurteiler gesetzt hatten, stieg die Anzeige ganz aus.
|
|
|
|
ZWEI GETRENNTE DINGE, die vorher eins waren:
|
|
* WAS ER TUN SOLL -- die Zuteilung. Die gibt es, bevor jemand
|
|
etwas bewertet, und sie gehoert ihm.
|
|
* WAS MAN SIEHT -- die Bewertung. Die erscheint erst, wenn
|
|
alle hingesehen haben, und das bleibt so.
|
|
======================================================================= */
|
|
console.log("");
|
|
console.log("=== VanVans Meldung: die eigene Karte kennt die Aufgaben ===");
|
|
{
|
|
const PA = "e-chat-neue-begruesst";
|
|
const PB = "e-chat-fragen-beantwortet";
|
|
|
|
/* Sauberer Ausgangspunkt -- die Abschnitte davor haben gesetzt. */
|
|
await hole(`/workspace/api/entwicklung/person/${idModi}/punkte`,
|
|
{ host: CREW, keks: dogi.keks, methode: "PUT",
|
|
rumpf: JSON.stringify({ punkte: [PA, PB] }) });
|
|
|
|
const meine = async () => (await hole("/workspace/api/entwicklung/meine-karte",
|
|
{ host: CREW, keks: ayla.keks })).daten;
|
|
|
|
const m0 = await meine();
|
|
const alle0 = (m0?.bloecke || []).flatMap((b) => b.punkte);
|
|
ok(alle0.some((p) => p.schluessel === PA && p.zugetragen === true),
|
|
"die eigene Karte weiss, welche Punkte zugetragen sind");
|
|
ok(m0?.meine_aufgaben === 2,
|
|
`und sagt, wie viele es sind (${m0?.meine_aufgaben})`);
|
|
|
|
/* DER KERN DER MELDUNG: Er sieht seine Aufgabe, BEVOR irgendjemand
|
|
sie bewertet hat. Vorher war das umgekehrt -- ohne vollstaendige
|
|
Bewertung sah er gar nichts. */
|
|
ok(alle0.find((p) => p.schluessel === PA)?.offen === true,
|
|
"sie steht da, obwohl noch niemand etwas dazu gesetzt hat");
|
|
|
|
/* GEGENPROBE: Ein Punkt, der NICHT zugetragen ist, traegt die Marke
|
|
auch nicht. Ohne sie hiesse „zugetragen=true" womoeglich nur,
|
|
dass dort immer `true` steht. */
|
|
const fremd = alle0.find((p) => p.schluessel !== PA && p.schluessel !== PB);
|
|
ok(fremd && fremd.zugetragen === false,
|
|
`ein nicht zugetragener Punkt traegt die Marke nicht (${fremd?.schluessel})`);
|
|
|
|
/* ---- Und die andere Haelfte: sieht die Leitung, dass es bei ihm
|
|
noch nicht ankommt? ------------------------------------- */
|
|
await hole(`/workspace/api/entwicklung/person/${idModi}/${PA}`,
|
|
{ host: CREW, keks: hand.keks, methode: "PUT",
|
|
rumpf: JSON.stringify({ stufe: "laeuft", anlass: "Im Stream gesehen." }) });
|
|
|
|
const sicht = async (keks) => {
|
|
const k = (await hole(`/workspace/api/entwicklung/person/${idModi}`,
|
|
{ host: CREW, keks })).daten;
|
|
return (k?.bloecke || []).flatMap((b) => b.punkte).find((p) => p.schluessel === PA);
|
|
};
|
|
|
|
const beiVan = await sicht(hand.keks);
|
|
ok(beiVan?.sieht_er && beiVan.sieht_er.da === 1 && beiVan.sieht_er.noetig === 2,
|
|
`VanVan sieht: eine von zwei Einschaetzungen liegt vor `
|
|
+ `(${JSON.stringify(beiVan?.sieht_er)})`);
|
|
|
|
const m1 = await meine();
|
|
ok((m1?.bloecke || []).flatMap((b) => b.punkte)
|
|
.find((p) => p.schluessel === PA)?.offen === true,
|
|
"und bei ihm ist der Punkt weiterhin offen -- eine halbe Karte waere schlimmer");
|
|
|
|
/* GEGENPROBE, OHNE DIE DIE ZWEI ZEILEN OBEN NICHTS BEWEISEN:
|
|
Setzt der zweite Beurteiler auch, geht es durch -- und die
|
|
Leitung sieht das ebenfalls. */
|
|
await hole(`/workspace/api/entwicklung/person/${idModi}/${PA}`,
|
|
{ host: CREW, keks: dogi.keks, methode: "PUT",
|
|
rumpf: JSON.stringify({ stufe: "laeuft", anlass: "Sehe ich auch so." }) });
|
|
|
|
const beiVan2 = await sicht(hand.keks);
|
|
ok(beiVan2?.sieht_er?.da === 2,
|
|
`nach der zweiten Einschaetzung steht es auf 2 von 2 `
|
|
+ `(${JSON.stringify(beiVan2?.sieht_er)})`);
|
|
|
|
const m2 = await meine();
|
|
const pa2 = (m2?.bloecke || []).flatMap((b) => b.punkte)
|
|
.find((p) => p.schluessel === PA);
|
|
ok(pa2?.offen === false && pa2?.zugetragen === true,
|
|
`jetzt sieht er die Einschaetzung -- und es ist weiterhin seine Aufgabe `
|
|
+ `(offen=${pa2?.offen}, zugetragen=${pa2?.zugetragen})`);
|
|
ok(Array.isArray(pa2?.antworten) && pa2.antworten[0]?.anzahl === 2,
|
|
`mit beiden Antworten (${JSON.stringify(pa2?.antworten)})`);
|
|
}
|
|
|
|
d.close();
|
|
try { rmSync(ordner, { recursive: true, force: true }); } catch { /* egal */ }
|
|
console.log("");
|
|
console.log(`${fehler === 0 ? "BESTANDEN" : "FEHLGESCHLAGEN"} -- ${geprueft} geprueft, ${fehler} Fehler`);
|
|
process.exit(fehler ? 1 : 0);
|