Entwicklung & Nachwuchs -- zwei Bretter fuer DogFather und die rechte Hand

Filipe: "ich will dass ich genau so eine kategorie habe in der dogfather
und rechten hand rollen, wo wir aufgaben oder bewertungen ueber modis
eingeben koennen. auch fuer zuschauer die vielleicht modis werden
koennten waere auch geil. mach dich schlau informier dich so krass wie
es nur geht, hol die besten der besten und krassesten sachen,
perfektionnier die dan auch alle und dan erst setzt du alles um."

=== WAS DIE RECHERCHE ERGEBEN HAT ===

DREI BEFUNDE HABEN DAS DESIGN BESTIMMT, und der erste hat es fast
umgedreht:

  WARUM MODERATOREN AUFHOEREN (Schoepke-Gonzalez u. a., New Media &
  Society 2024): zwei Hauptgruende -- zu wenig Zeit, und Konflikte im
  Team beziehungsweise schaedliches Verhalten der Leitung. Eine
  Bewertungsfunktion ist damit genau das Werkzeug, mit dem man ein Team
  verliert, wenn man sie als Ueberwachung baut. Das ist kein Bauchgefuehl
  und keine Zimperlichkeit -- es ist der haeufigste gemessene Grund.

  WAS HAELT: Anerkennung. Dank und Rueckmeldung erhoehen die
  Verweildauer messbar; Rollenklarheit senkt Burnout.

  WORAN MAN GUTE MODERATOREN ERKENNT (ModSquad, Kitfox Games,
  Discord-Leitfaeden): nicht an Zahlen. Ruhig bleiben, wenn es hitzig
  wird; von selbst helfen; die Regeln UND die Leute kennen; regelmaessig
  da sein. Ausdruecklich NICHT: "schreibt viel" -- angenehm im Chat zu
  sein ist nachweislich etwas anderes. Und der treffsicherste Weg
  ueberhaupt ist die Empfehlung aus dem Team, gefolgt von einer
  Probezeit.

=== WAS DARAUS GEBAUT WURDE ===

ENTWICKLUNG. Keine Note, sondern eine Aufzeichnung ueber die Zeit, je
Person. Fuenf Arten, und ihre REIHENFOLGE ist Absicht: "Das laeuft gut"
und "Danke dafuer" stehen vorn. Wer ein Formular oeffnet, dessen erstes
Feld "Problem" heisst, schreibt Probleme auf.

"ZU VIEL GERADE" IST DIE WICHTIGSTE ART und die, die es sonst nirgends
gibt. Der haeufigste Grund zu gehen ist Zeitmangel, und der zeigt sich
frueh -- nur schreibt ihn niemand auf, weil es kein Feld dafuer gibt.

TALENTE. Die vier Merkmale sind die aus der Literatur, nicht
ausgedacht, dazu die Empfehlung aus dem Team. Der Status IST die
Probezeit: `offen` heisst beobachtet, `angenommen` heisst angesprochen
-- und was daraus wird, entscheidet sich in "Personen & Zugaenge" mit
einem Zugang auf Stufe "Probe". Eine eigene Kandidatentabelle waere ein
zweiter Ort fuer dieselbe Frage.

DIE BRUECKE. Die Person sieht den Entwicklungs-Bereich NICHT -- eine
halb sichtbare Akte ist schlimmer als eine geschlossene, weil niemand
mehr weiss, was der andere gerade liest. Damit Anerkennung trotzdem
ankommt, laesst sich jeder Eintrag EINMAL als Nachricht in den Chat
schicken, mit Art, Titel und dem, was daraus folgen soll. Danach steht
im Eintrag, wann es geschehen ist: Die Frage "habe ich ihr das
eigentlich schon gesagt?" beantwortet man nach zwei Wochen falsch.

ZWEI KACHELN UND NICHT DREI: Aufgaben an das Team gibt es laengst, mit
Person, Frist und Status. Eine zweite Stelle dafuer waere ein zweiter
Ort, an dem man nachsehen muesste, welche Aufgabe wirklich gilt.

DIE GRUPPE HEISST NICHT "TEAM FUEHREN". Das waere der bequeme Name und
der falsche: Im Haus gilt, dass DogFather nicht ueber seinem Team steht.
"Entwicklung & Nachwuchs" sagt, was drinsteht.

=== KEIN NEUER BAUKASTEN ===

Beides sind BEREICHE wie das Ideen-Board: dieselbe Tabelle, dieselbe
Seite, dieselben Regeln fuers Anlegen, Aendern und Loeschen. Neu sind
eine Spalte (`gesendet_am`) und eine Route. Die Sendefunktion selbst
steht im Chat-Modul und nicht daneben: Ein Zweier-Gespraech darf es nur
einmal geben, die Leseraender muessen mitwandern, Live-Strom und
Benachrichtigung haengen daran -- wer das nachbaut, hat zwei Fassungen,
und die zweite ist die, in der jemand eine Nachricht nicht bekommt.

=== WAS DIE PRUEFUNG GEFUNDEN HAT ===

"Zugeordneter Creator existiert nicht" -- beim Anlegen eines Eintrags
ueber ein Teammitglied. Die Regel verlangte einen Creator; bei
Entwicklung geht es um Menschen aus dem Team. Die Meldung war dabei
selbst irrefuehrend: Die Person existiert sehr wohl, sie ist nur kein
Creator. Beides behoben.

Und meine EIGENE Pruefung von vor einer Stunde wurde rot: Sie verlangte,
dass jede Zusatzkachel in der Gruppe "Team Dogi" steht. Die naheliegende
Reparatur waere gewesen, die Gruppe gar nicht mehr zu pruefen -- das
haette die Aussage weggeworfen. Jetzt steht die erlaubte Liste
ausgeschrieben da: Eine Kachel fuer zwei Menschen darf nicht in einer
Gruppe landen, die alle sehen. Kommt eine dritte Gruppe, wird die Zeile
rot, und das ist Absicht.

Die zwei neuen Farbtoene trennen ueber Saettigung und Helligkeit statt
ueber den Farbwinkel -- bei 25 Toenen ist der Kreis voll, und die
groessten Luecken waeren ein drittes Gruen zwischen zwei vorhandenen.
Bei einer achtundzwanzigsten Kachel brauchen die Kacheln Gruppenfarben
statt Einzelfarben; das steht als Notiz im Quelltext.

pruef-entwicklung 33 (neu) · pruef-start-ansicht 27 Kacheln, sechs
Gruppen · pruef-rollen 278 -> 282 · pruef-modi-checkliste 70 ·
pruef-haus-trennung 62 · pruef-rueckmeldung 30 · pruef-modi-ideen 30 ·
pruef-modi-verborgen 80 · pruef-css-klassen gruen · pruef-modi-wortleck 5.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
2026-09-11 01:11:30 +02:00
co-authored by Claude Opus 5
parent bea1bb7194
commit 9b47bd3ca0
33 changed files with 1029 additions and 294 deletions
+237 -3
View File
@@ -18,8 +18,9 @@ import express from "express";
import {
db, protokolliere, echteIp, sitzungLesen, betreutWo, darfCreator, betreuteIds, istLeitung, istDogFather, siehtAlles, istSpicy, ohneDogFather,
externPruefen, externSql,
ohneTeamDogi, ohneAgentur, siehtModis, MODI_BEREICHE_ERLAUBT, TEAM_DOGI_ROLLEN,
ohneTeamDogi, ohneAgentur, siehtModis, fuehrtTeamDogi, MODI_BEREICHE_ERLAUBT, TEAM_DOGI_ROLLEN,
} from "./workspace.js";
import { nachrichtSchicken } from "./workspace-chat.js";
export const bereicheRouter = express.Router();
@@ -173,6 +174,104 @@ export const BEREICHE = {
Saetzen erkennt jeder jeden. Ein Versprechen, das nicht haelt, ist
schlimmer als keines. Was es stattdessen gibt, ist die Wahl
zwischen "fuers Team" und "nur an DogFather". */
/* =====================================================================
ENTWICKLUNG (11.09.2026)
Filipe: "wo wir aufgaben oder bewertungen ueber modis eingeben
koennen."
ES IST KEINE NOTE, UND DAS IST KEINE ZIMPERLICHKEIT, SONDERN DAS
ERGEBNIS DER RECHERCHE. Die groesste Untersuchung dazu (Schoepke-
Gonzalez u. a., New Media & Society 2024, ueber 500 befragte
Freiwillige) nennt zwei Hauptgruende, warum Moderatoren aufhoeren:
zu wenig Zeit -- und Konflikte im Team beziehungsweise schaedliches
Verhalten der Leitung. Eine Bewertungsfunktion ist damit genau das
Werkzeug, mit dem man ein Team verliert, wenn sie als Ueberwachung
gebaut ist.
Was HAELT, ist ebenso gut belegt: Anerkennung. Dank und
Rueckmeldung erhoehen die Verweildauer messbar; Rollenklarheit
senkt Burnout.
DARAUS FOLGEN DIE FUENF ARTEN, und ihre Reihenfolge ist Absicht:
Das Erste, was man sieht, ist "Das laeuft gut". Wer ein Formular
oeffnet, dessen erstes Feld "Problem" heisst, schreibt Probleme
auf.
"ZU VIEL GERADE" IST DIE WICHTIGSTE ART und die, die es sonst
nirgends gibt. Der haeufigste Grund zu gehen ist Zeitmangel, und
der zeigt sich frueh -- nur schreibt ihn niemand auf, weil es
kein Feld dafuer gibt. Jetzt gibt es eins.
DIE PERSON SIEHT DEN BEREICH NICHT. Eine halb sichtbare Akte ist
schlimmer als eine geschlossene: Niemand weiss mehr, was der
andere gerade liest. Damit Anerkennung trotzdem ankommt, laesst
sich jeder Eintrag als Nachricht an die Person schicken -- ein
ausdruecklicher Schritt, und im Eintrag steht danach, wann er
getan wurde.
===================================================================== */
entwicklung: {
name: "Entwicklung",
ober: "Über die Zeit",
arten: {
staerke: "Das läuft gut",
dank: "Danke dafür",
wachsen: "Daran arbeiten wir",
last: "Zu viel gerade",
absprache: "So haben wir es vereinbart",
},
bewertung: false,
dringlichkeit: true,
dringlichkeitName: "Wie dringend",
/* Der Personenbezug ist hier der SINN des Eintrags und nicht eine
Nebenangabe -- deshalb ausdruecklich KEIN `ohneCreatorBezug`. */
einsatzFeld: true,
einsatzName: "Was daraus folgen soll",
},
/* =====================================================================
TALENTE (11.09.2026)
Filipe: "auch fuer zuschauer die vielleicht modis werden koennten
waere auch geil."
DIE VIER MERKMALE SIND NICHT AUSGEDACHT. Sie sind das, was sich in
der Literatur zu Moderatorenauswahl immer wieder findet (ModSquad,
Kitfox Games, Discord-Leitfaeden): ruhig bleiben, wenn es hitzig
wird; von selbst helfen; die Regeln UND die Leute kennen; und
regelmaessig da sein. Ausdruecklich NICHT dabei: "schreibt viel"
-- angenehm im Chat zu sein ist nachweislich etwas anderes, als
ein guter Moderator zu sein.
Und der treffsicherste Weg ueberhaupt ist der fuenfte: die
EMPFEHLUNG aus dem Team. Wer schon dabei ist, hat den Menschen im
Alltag erlebt, nicht in einem Bewerbungsformular.
DER STATUS IST DIE PROBEZEIT. `offen` heisst beobachtet,
`angenommen` heisst angesprochen -- und was daraus wird,
entscheidet sich in "Personen & Zugaenge" mit einem Zugang auf
Stufe "Probe". Eine eigene Kandidatentabelle waere ein zweiter Ort
fuer dieselbe Frage.
===================================================================== */
talente: {
name: "Talente",
ober: "Nachwuchs",
arten: {
ruhig: "Bleibt ruhig, wenn es hitzig wird",
hilft: "Hilft anderen von selbst",
kennt: "Kennt die Regeln und die Leute",
da: "Ist regelmäßig da",
empfohlen: "Aus dem Team empfohlen",
},
bewertung: true,
bewertungName: "Wie sicher bin ich",
dringlichkeit: true,
dringlichkeitName: "Wie eilig",
ohneCreatorBezug: true,
einsatzFeld: true,
einsatzName: "Wo ich ihn oder sie gesehen habe",
},
rueckmeldung: {
name: "Rückmeldung",
/* Wie beim Ideen-Board: Das hier ist keine Akte ueber jemanden,
@@ -339,6 +438,31 @@ for (const geschuetzt of ["ideen", "angebote", "rueckmeldung"]) {
});
}
/* ---------------------------------------------------------------------
ZWEI BEREICHE GEHEN NUR DOGFATHER UND DIE RECHTE HAND AN (11.09.2026)
Filipe: "wo WIR aufgaben oder bewertungen ueber modis eingeben
koennen." Wir heisst hier: die beiden.
`siehtModis` waere zu weit -- darin sind die Modis selbst enthalten,
und in "Entwicklung" steht, was ueber sie aufgeschrieben wird.
`fuehrtTeamDogi` ist die engere Menge und dieselbe, die schon den
Eingang und die Kanaele traegt.
404 UND NICHT 403: Ein Modi soll nicht erfahren, dass es diese
Bereiche gibt. Ein "das darfst du nicht" waere die Auskunft, die
genau die Frage aufwirft, die niemand stellen soll.
ZWEI SCHLOESSER WIE OBEN: Hier haengt der ZUGANG, die Sichtbarkeit
der Zeilen haengt an sichtbarEintrag(). Faellt eines weg, haelt das
andere. */
for (const nurLeitung of ["entwicklung", "talente"]) {
bereicheRouter.use(`/workspace/api/bereich/${nurLeitung}`, (req, res, next) => {
if (fuehrtTeamDogi(req.person)) return next();
return res.status(404).json({ fehler: "nicht_gefunden" });
});
}
/* ---------------------------------------------------------------------
UND UMGEKEHRT: Ein Modi kommt nur in SEINE Bereiche.
@@ -629,6 +753,11 @@ const SPALTEN = `
andere -- und beim naechsten Mal schriebe er offen, was er
vertraulich meinte. */
e.nur_leitung,
/* Wann der Eintrag der Person geschickt wurde -- oder NULL. Die
Oberflaeche baut daraus entweder einen Knopf oder einen Satz; ohne
diese Angabe koennte sie nur einen Knopf zeigen, der beim zweiten
Druecken eine Absage bringt. */
e.gesendet_am,
${externSql("pc.name", "e.creator_extern")} AS creator_name,
pe.name AS erstellt_name,
(SELECT name FROM content_saeulen s WHERE s.id = e.saeule_id) AS saeule_name,
@@ -675,6 +804,88 @@ bereicheRouter.get("/workspace/api/bereich/:bereich", (req, res) => {
}
});
/* =====================================================================
EINEN EINTRAG AN DIE PERSON SCHICKEN (11.09.2026)
Der Entwicklungs-Bereich ist eine Aufzeichnung fuer DogFather und die
rechte Hand; die Person selbst sieht ihn nicht. Diese Route ist die
einzige Bruecke -- und sie geht nur in EINE Richtung, ausdruecklich
und einzeln.
WARUM NICHT EINFACH ALLES SICHTBAR MACHEN: Eine halb sichtbare Akte
ist schlimmer als eine geschlossene. Niemand weiss mehr, was der
andere gerade liest, und man schreibt anders -- vorsichtiger,
weniger, oder gar nicht mehr.
WARUM ES DIESE BRUECKE TROTZDEM BRAUCHT: Die Forschung zu
freiwilligen Moderatoren ist an dieser Stelle eindeutig. Was Menschen
haelt, ist Anerkennung -- Dank und Rueckmeldung erhoehen die
Verweildauer messbar. Ein "Das laeuft gut", das nur in einer Akte
steht, hat niemandem geholfen.
DER WEG IST DER CHAT und keine neue Benachrichtigungsart: Die Person
findet die Nachricht dort, wo sie alles andere auch findet, kann
antworten, und sie steht in ihrem Verlauf. Ein eigener Kanal dafuer
waere ein zweiter Ort mit einer roten Zahl.
EINMAL IST EINMAL. `gesendet_am` haelt fest, wann es geschehen ist --
die Route schickt danach nicht noch einmal. Zweimal dasselbe Lob ist
kein doppeltes Lob, sondern ein Versehen, das jeder bemerkt.
===================================================================== */
bereicheRouter.post("/workspace/api/bereich/entwicklung/:id/senden",
gleicheHerkunft, (req, res) => {
try {
if (!fuehrtTeamDogi(req.person)) return res.status(404).json({ fehler: "nicht_gefunden" });
const id = Number(req.params.id);
if (!Number.isInteger(id)) return res.status(400).json({ fehler: "ungueltig" });
const e = db().prepare(
`SELECT id, bereich, art, titel, text, einsatz, creator_id, gesendet_am
FROM eintraege WHERE id = ?`).get(id);
if (!e || e.bereich !== "entwicklung") {
return res.status(404).json({ fehler: "nicht_gefunden" });
}
if (!e.creator_id) {
return res.status(400).json({
fehler: "Dieser Eintrag gehört zu niemandem – trag zuerst ein, um wen es geht.",
});
}
if (e.gesendet_am) {
return res.status(409).json({ fehler: "Das ist schon geschickt.", am: e.gesendet_am });
}
/* DER TEXT WIRD GEBAUT UND NICHT ROH GESCHICKT. Im Bereich steht
eine Notiz ("Ruhig geblieben, als es kippte"); als Nachricht
ohne Zusammenhang liest sich das wie ein Fetzen. Die Art davor
sagt, was gemeint ist, und der Einsatz dahinter, was daraus
folgen soll -- beides steht ohnehin schon da. */
const art = BEREICHE.entwicklung.arten[e.art] || "";
const stuecke = [
art ? `${art}: ${e.titel}` : e.titel,
e.text || "",
e.einsatz ? `Was daraus folgen soll: ${e.einsatz}` : "",
].filter(Boolean);
const geschickt = nachrichtSchicken(req.person, e.creator_id, stuecke.join("\n\n"));
if (!geschickt) {
return res.status(400).json({
fehler: "Die Nachricht ging nicht raus – ist die Person noch aktiv?",
});
}
const am = jetzt();
db().prepare("UPDATE eintraege SET gesendet_am = ? WHERE id = ?").run(am, id);
protokolliere("entwicklung_geschickt", {
personId: req.person.id, rolle: req.person.rolle, ip: echteIp(req),
detail: `#${id} an #${e.creator_id}`,
});
res.json({ ok: true, am, raum_id: geschickt.raum_id });
} catch (fehler) {
console.error("[workspace] Entwicklung schicken:", fehler?.message);
res.status(503).json({ fehler: "nicht_verfuegbar" });
}
});
/* ---------- Prüfen ------------------------------------------------------- */
function pruefe(bereich, körper, { neu }) {
@@ -875,9 +1086,32 @@ bereicheRouter.post("/workspace/api/bereich/:bereich", gleicheHerkunft, (req, re
aus.creator_id = erster;
}
}
/* WEM GEHOERT DER EINTRAG? DAS HAENGT AM BEREICH (11.09.2026).
Ueberall sonst zeigt `creator_id` auf einen Creator -- die
Bereichsseiten sind Betreuungsakten. Im Entwicklungs-Bereich
zeigt sie auf ein Mitglied von Team Dogi: Dort steht, was ueber
die Zeit an einem Menschen aus dem Team auffaellt.
Die Spalte heisst weiterhin creator_id. Das ist Geschichte, kein
Inhalt -- sie haelt eine Personennummer, und dieselbe
Ueberlegung steht schon in workspace-checkliste.js.
BEIM ERSTEN ANLAUF FEHLTE DAS, und die Meldung war irrefuehrend
("Zugeordneter Creator existiert nicht" -- die Person existiert
sehr wohl, sie ist nur kein Creator). Gefunden von der Pruefung,
nicht vom Auge. */
const zielRollen = bereich === "entwicklung"
? "rolle IN ('hand','modi')"
: "rolle = 'creator'";
if (aus.creator_id
&& !db().prepare("SELECT 1 FROM personen WHERE id = ? AND rolle = 'creator'").get(aus.creator_id)) {
return res.status(400).json({ fehler: "Zugeordneter Creator existiert nicht." });
&& !db().prepare(`SELECT 1 FROM personen WHERE id = ? AND ${zielRollen}`)
.get(aus.creator_id)) {
return res.status(400).json({
fehler: bereich === "entwicklung"
? "Diese Person gehört nicht zum Team."
: "Zugeordneter Creator existiert nicht.",
});
}
/* Eine Saeule gehoert immer GENAU EINEM Creator. Ohne diese Pruefung