Der Aufgabenkatalog fuers Team: Kategorie mal Stufe, 88 Aufgaben

Filipe: "perfektionniere die kategorien wo ich aufgaben an die modis
verteile und so, ich will dass du alles was es gibt auf der welt durch
gehst und wie bei den aufgaben wo sie fuer mich haben auch mit
kategorien und vielen aufgaben."

ERST GEZAEHLT, WAS DA WAR. Es gab einen Katalog: 60 Eintraege in neun
Phasen. Wem sie gehoerten:

  DogFather                13
  DogFather & rechte Hand  12
  rechte Hand              14
  --------------------------------
  zusammen                 39 von 60

Zwei Drittel des "Modi-Katalogs" waren Aufbauarbeiten fuer Filipe
selbst. Nur 20 Eintraege waren wirklich Arbeit fuer jemanden im Team.
Und die Verteilung ueber die vierzehn Kategorien war schief: Planung
10, Events 1, Wachstum 1, Branding 1, Sonstiges 0.

Der Aufbauplan ist nicht falsch, nur etwas anderes -- er bleibt unter
`aufbauplan` erhalten. Ihn zu loeschen hiesse, 60 durchdachte Schritte
wegzuwerfen, weil sie am falschen Platz standen.

NEU: 88 Aufgaben in KATEGORIE mal STUFE, dieselbe Form wie bei den
Creator-Vorlagen. Wer eine Aufgabe vergibt, denkt "Frida macht Chat"
und nicht "wir sind in Phase 3".

  Chat 11, Team 8, Community/Events/Clipping/Technik je 7,
  Social/Planung/Organisation je 6, Kommunikation/Analyse/Wachstum/
  Branding je 5, Sonstiges 3 -- keine Kategorie mehr leer.

  Drei Stufen: neu dabei (24), eingearbeitet (35), erfahren (29).
  Eine Aufgabe der Stufe "erfahren" an einen Neuen zu geben ist kein
  Kompliment, sondern ein Ueberfallen.

UND DIE VIERZEHN KATEGORIEN HABEN JETZT EINEN SATZ. Vorher standen da
vierzehn nackte Namen -- "Organisation" und "Planung" nebeneinander,
ohne dass jemand sagt, was worin gehoert. Dann landet dieselbe Aufgabe
beim einen unter Planung, beim anderen unter Organisation, und jede
Auswertung darueber ist wertlos. Jetzt: Planung ist, was NOCH NICHT
ist; Organisation, was bereits ist, in Ordnung zu halten.

RECHERCHIERT, NICHT AUSGEDACHT (Quellen im Kopf des Katalogs):
Twitch und Discord zu dem, was ein Moderator tatsaechlich tut; TikTok
LIVE im Besonderen (gefilterte Kommentare, Gaesteverwaltung, Regeln zu
Beginn, Matches, Geschenke ohne Betteln); Community-Arbeit zu Rhythmus,
Vertretung und Monatsrueckblick. Dazu die Burnout-Forschung, die schon
in "Wie geht's dir?" steht -- deshalb stehen unter "Team" Aufgaben, die
zu wenig Zeit und Streit frueh sichtbar machen.

WAS DABEI BEINAHE SCHIEFGEGANGEN WAERE, und was es gefunden hat:

  Das Uebernehmen griff noch auf MODI_KATALOG zu -- die alten Phasen.
  Der Browser schickt die Nummer aus der AUSGELIEFERTEN Liste zurueck.
  Ein Klick auf "Uebernehmen" haette damit eine voellig andere Aufgabe
  angelegt, und zwar eine, die es gibt: keine Fehlermeldung, nichts
  Rotes, nur die falsche Aufgabe auf dem Brett. Gefunden hat das
  pruef-modi-katalog, die an der verschwundenen Phase abgestuerzt ist.

  Der Knopf "Alle N uebernehmen" schickte die Stufe nicht mit. Er sagte
  "Alle 4 uebernehmen" und haette elf angelegt -- das merkt man erst
  auf dem Brett.

  Und der Satz ueber dem Brett sagte weiterhin "Nach Etappen sortiert".
  Gesehen im Bildschirmfoto der Pruefung, nicht im Code.

  server/pruef-modi-katalog.mjs   36 Pruefungen, 0 Fehler
  Neu darin: jede Kategorie muss belegt sein (mindestens drei), jede
  Stufe auch, keine Kennung doppelt -- und JEDER TEXT MUSS BEGRUENDEN.
  Die letzte Zeile hat zwei meiner eigenen Texte als zu duenn erwischt.

  pruef-modi-kategorien 25, pruef-aufgabenbrett, pruef-vorlagen,
  pruef-css-klassen, pruef-struktur -- alle 0 Fehler

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
2026-09-16 19:05:34 +02:00
co-authored by Claude Opus 5
parent 99f96553c5
commit 4802954263
35 changed files with 1035 additions and 413 deletions
+84 -30
View File
@@ -99,24 +99,58 @@ let katalog = null;
{
const a = await hole(modiAn.keks, "/workspace/api/vorlagen");
katalog = a.daten?.katalog || null;
/* SECHZIG, fest hingeschrieben. Das Dokument wirbt mit "60+ konkrete
Aufgaben"; faellt eine heraus, muss das auffallen. Eine Zahl, die
sich selbst nachzaehlt, kann das nicht. */
ok(katalog && katalog.aufgaben.length === 60,
`${katalog?.aufgaben?.length ?? 0} Aufgaben (erwartet 60)`);
ok(katalog && katalog.gruppen.length === 9,
`${katalog?.gruppen?.length ?? 0} Etappen (erwartet 9)`);
/* Die Verteilung aus Teil 2, Phase fuer Phase. Sie steht hier, weil
eine reine Gesamtzahl auch dann stimmt, wenn zwoelf Aufgaben in der
falschen Etappe liegen. */
const soll = { aufbau: 12, alltag: 9, vor: 5, waehrend: 5, nach: 4,
content: 8, woche: 5, monat: 5, community: 7 };
const ist = {};
for (const v of katalog?.aufgaben || []) ist[v.gruppe] = (ist[v.gruppe] || 0) + 1;
ok(JSON.stringify(ist) === JSON.stringify(soll),
`die Verteilung stimmt (${Object.entries(ist).map(([k, n]) => `${k}:${n}`).join(" ")})`);
ok((katalog?.aufgaben || []).every((v) => v.titel && v.text && v.kategorie && v.rolle && v.tage),
"jede Aufgabe hat Titel, Text, Kategorie, empfohlene Rolle und Frist");
/* SEIT DEM 16.09.2026 NACH KATEGORIE UND STUFE.
Vorher standen hier neun PHASEN mit 60 Eintraegen -- und
nachgezaehlt gehoerten 39 davon DogFather und der rechten Hand.
Es war ein Aufbauplan, kein Arbeitskatalog. Der Aufbauplan gibt es
weiterhin, unter `aufbauplan`; hier steht jetzt, was ein Mensch im
Team TUT.
ZAHLEN FEST HINGESCHRIEBEN, wie vorher: Eine Zahl, die sich selbst
nachzaehlt, merkt nicht, wenn etwas herausfaellt. */
ok(katalog && katalog.gruppen.length === 14,
`${katalog?.gruppen?.length ?? 0} Kategorien (erwartet 14)`);
ok(katalog && katalog.stufen.length === 3,
`${katalog?.stufen?.length ?? 0} Stufen (erwartet 3)`);
ok(katalog && katalog.aufgaben.length === 88,
`${katalog?.aufgaben?.length ?? 0} Aufgaben (erwartet 88)`);
/* JEDE KATEGORIE MUSS BELEGT SEIN. Vorher hatten vier von vierzehn
eine Aufgabe oder keine (Events 1, Wachstum 1, Branding 1,
Sonstiges 0) -- eine Kategorie ohne Inhalt ist ein leeres
Versprechen im Auswahlfeld. */
const je = {};
for (const v of katalog?.aufgaben || []) je[v.gruppe] = (je[v.gruppe] || 0) + 1;
const leer = (katalog?.gruppen || []).filter((g) => !je[g.schluessel]);
ok(leer.length === 0,
`jede Kategorie hat Aufgaben${leer.length ? ": leer sind " + leer.map((g) => g.name).join(", ") : ""}`);
ok(Math.min(...Object.values(je)) >= 3,
`auch die kleinste hat mindestens drei (${Math.min(...Object.values(je))})`);
/* UND JEDE STUFE. Eine Stufe ohne Aufgaben waere ein Knopf, der eine
leere Liste oeffnet. */
const jeStufe = {};
for (const v of katalog?.aufgaben || []) jeStufe[v.stufe] = (jeStufe[v.stufe] || 0) + 1;
ok((katalog?.stufen || []).every((s) => jeStufe[s.schluessel] > 0),
`jede Stufe ist belegt (${Object.entries(jeStufe).map(([k, n]) => `${k}:${n}`).join(" ")})`);
ok((katalog?.aufgaben || []).every((v) => v.titel && v.text && v.gruppe && v.stufe && v.tage),
"jede Aufgabe hat Titel, Text, Kategorie, Stufe und Frist");
/* DER TEXT SAGT WARUM, nicht was. Ein Text, der kuerzer ist als der
Titel, ist eine Wiederholung -- und die haette man sich sparen
koennen. */
const duenn = (katalog?.aufgaben || []).filter((v) => v.text.length < v.titel.length + 20);
ok(duenn.length === 0,
`jeder Text begruendet${duenn.length ? ": zu duenn bei " + duenn[0].schluessel : ""}`);
ok(new Set((katalog?.aufgaben || []).map((v) => v.schluessel)).size
=== (katalog?.aufgaben || []).length, "keine Kennung doppelt");
/* DER AUFBAUPLAN IST NICHT VERLORENGEGANGEN. */
const plan = a.daten?.aufbauplan;
ok(plan && plan.aufgaben.length === 60 && plan.gruppen.length === 9,
`der Aufbauplan steht weiter zur Verfuegung (${plan?.aufgaben?.length ?? 0} in `
+ `${plan?.gruppen?.length ?? 0} Etappen)`);
}
/* Jede Kategorie im Katalog muss es auch wirklich geben -- sonst nimmt
@@ -124,7 +158,11 @@ let katalog = null;
{
const a = await hole(modiAn.keks, "/workspace/api/aufgaben");
const gueltig = new Set((a.daten?.kategorien || []).map((k) => k.wert));
const unbekannt = [...new Set((katalog?.aufgaben || []).map((v) => v.kategorie))]
/* Die Kategorie heisst im ausgelieferten Katalog `gruppe` -- sie ist
zugleich die Gruppe, nach der die Oberflaeche sortiert. Ein
zweiter Name fuer dasselbe waere die Gelegenheit, dass beide
auseinanderlaufen. */
const unbekannt = [...new Set((katalog?.aufgaben || []).map((v) => v.gruppe))]
.filter((k) => !gueltig.has(k));
ok(gueltig.size > 0 && unbekannt.length === 0,
unbekannt.length
@@ -152,11 +190,11 @@ for (const [name, an] of [["Manager", manAn], ["Creator", creatAn]]) {
/* ---------- 3. Uebernehmen ------------------------------------------- */
console.log("\n=== 3. Aus der Vorlage wird eine echte Aufgabe ===\n");
{
const nr = katalog.aufgaben.findIndex((v) => v.gruppe === "waehrend");
const nr = katalog.aufgaben.findIndex((v) => v.gruppe === "chat");
const vorlage = katalog.aufgaben[nr];
const a = await hole(modiAn.keks, "/workspace/api/vorlagen/uebernehmen", {
method: "POST", headers: jsonKopf,
body: JSON.stringify({ art: "katalog", nr, gruppe: "waehrend" }),
body: JSON.stringify({ art: "katalog", nr, gruppe: "chat" }),
});
ok(a.status === 200 && a.daten?.angelegt === 1, `übernommen (${a.status})`);
@@ -166,30 +204,34 @@ console.log("\n=== 3. Aus der Vorlage wird eine echte Aufgabe ===\n");
/* DIE KATEGORIE IST DER HALBE WERT DER VORLAGE. Ginge sie unterwegs
verloren, muesste man 60-mal von Hand einsortieren, was im Dokument
danebensteht -- und niemand merkte es, weil die Aufgabe ja da ist. */
ok(neu?.kategorie === vorlage.kategorie,
`mit ihrer Kategorie (${neu?.kategorie} / erwartet ${vorlage.kategorie})`);
/* DIE KATEGORIE IST DER HALBE WERT DER VORLAGE -- und sie heisst im
ausgelieferten Katalog `gruppe`, weil die Oberflaeche damit
zeichnet. Ginge sie unterwegs verloren, muesste man von Hand
einsortieren, was danebensteht. */
ok(neu?.kategorie === vorlage.gruppe,
`mit ihrer Kategorie (${neu?.kategorie} / erwartet ${vorlage.gruppe})`);
ok(!!neu?.frist, `und mit Frist (${neu?.frist})`);
ok(neu?.verantwortlich_id === idModi, "und er selbst ist verantwortlich");
}
/* Eine ganze Etappe auf einmal. */
{
const wieViele = katalog.aufgaben.filter((v) => v.gruppe === "nach").length;
const wieViele = katalog.aufgaben.filter((v) => v.gruppe === "technik").length;
const a = await hole(modiAn.keks, "/workspace/api/vorlagen/uebernehmen", {
method: "POST", headers: jsonKopf,
body: JSON.stringify({ art: "katalog_alle", gruppe: "nach" }),
body: JSON.stringify({ art: "katalog_alle", gruppe: "technik" }),
});
ok(a.status === 200 && a.daten?.angelegt === wieViele,
`eine ganze Etappe auf einmal: ${a.daten?.angelegt} von ${wieViele}`);
`eine ganze Kategorie auf einmal: ${a.daten?.angelegt} von ${wieViele}`);
}
/* ---------- 4. DogFather traegt fuer jemanden ein -------------------- */
console.log("\n=== 4. DogFather trägt für einen Modi ein ===\n");
{
const nr = katalog.aufgaben.findIndex((v) => v.gruppe === "monat");
const nr = katalog.aufgaben.findIndex((v) => v.gruppe === "analyse");
const a = await hole(dogiAn.keks, "/workspace/api/vorlagen/uebernehmen", {
method: "POST", headers: jsonKopf,
body: JSON.stringify({ art: "katalog", nr, gruppe: "monat", person_id: idModi }),
body: JSON.stringify({ art: "katalog", nr, gruppe: "analyse", person_id: idModi }),
});
ok(a.status === 200 && a.daten?.angelegt === 1, `für den Modi geht es (${a.status})`);
@@ -259,14 +301,26 @@ console.log("\n=== 6. Das Brett im echten Browser ===\n");
const bild = await seite.evaluate(() => ({
sichtbar: !document.getElementById("vorlagenbrett")?.hidden,
etappen: [...document.querySelectorAll(".stufenleiter__knopf .stufenleiter__name")]
/* ZWEI REIHEN SEIT DEM 16.09.2026: oben die Kategorien, darunter
die Stufen. Getrennt gezaehlt -- eine gemeinsame Zahl haette
nicht gesagt, WELCHE der beiden fehlt. */
kategorien: [...document.querySelectorAll(
".stufenleiter:not(.stufenleiter--fein) .stufenleiter__name")]
.map((z) => z.textContent.trim()),
stufen: [...document.querySelectorAll(
".stufenleiter--fein .stufenleiter__name")].map((z) => z.textContent.trim()),
karten: document.querySelectorAll(".vorlagenbrett__karte").length,
text: document.body.innerText,
kopf: (document.querySelector(".kopfleiste .marke__text") || {}).textContent || "",
}));
ok(bild.sichtbar, "das Brett steht da");
ok(bild.etappen.length === 9, `mit neun Etappen (${bild.etappen.join(" · ")})`);
ok(bild.kategorien.length === 14,
`mit vierzehn Kategorien (${bild.kategorien.join(" · ")})`);
/* DREI STUFEN PLUS "Alle" -- der vierte Knopf ist die Ruecknahme.
Ohne ihn kaeme man aus einer Stufe nicht wieder heraus, ohne die
Kategorie zu wechseln. */
ok(bild.stufen.length === 4 && bild.stufen[0] === "Alle",
`und drei Stufen daneben (${bild.stufen.join(" · ")})`);
ok(bild.karten > 0, `und ${bild.karten} Karten in der offenen Etappe`);
/* Der Katalog des ANDEREN darf nirgends durchschlagen. */
ok(!/vier Bereichen und vier Stufen/.test(bild.text),