diff --git a/server/crew-adresse.js b/server/crew-adresse.js
index ff753e65..96b11afb 100644
--- a/server/crew-adresse.js
+++ b/server/crew-adresse.js
@@ -91,7 +91,9 @@ export const CREW_ADRESSE = "https://crew.dogfather-universe.com";
erst zur Laufzeit auf. Diese Datei hat bewusst KEINE eigenen
Importe; sie ist damit der einzige Ort, an dem so etwas gefahrlos
stehen kann. workspace.js gibt sie fuer die Fachmodule weiter. */
-export const TEAM_DOGI_ROLLEN = new Set(["hand", "modi"]);
+/* Die linke Hand ist Team Dogi wie die rechte (21.09.2026) -- sie
+ fuehrt dieselben Leute durch denselben Tag. */
+export const TEAM_DOGI_ROLLEN = new Set(["hand", "linke", "modi"]);
/* WER VON AUSSEN KOMMT (11.09.2026).
diff --git a/server/pruef-rechtetafel.mjs b/server/pruef-rechtetafel.mjs
index 30079ed0..e8db534c 100644
--- a/server/pruef-rechtetafel.mjs
+++ b/server/pruef-rechtetafel.mjs
@@ -95,8 +95,17 @@ melde("=== 1. Jede Rolle und jede Seite hat einen Eintrag ===");
const ausWorkspace = [...(w.ROLLEN_NAME ? Object.keys(w.ROLLEN_NAME) : [])].sort();
const ausTafel = [...rechte.ALLE_ROLLEN].sort();
ok(ausWorkspace.length > 0, `workspace.js kennt ${ausWorkspace.length} Rollen`);
- ok(JSON.stringify(ausWorkspace) === JSON.stringify(ausTafel),
- `beide Listen sind identisch (${ausTafel.join(", ")})`);
+ /* DIE MELDUNG MUSS SAGEN, WAS FEHLT -- nicht nur, dass etwas fehlt.
+ Vorher stand hier nur EINE der beiden Listen; beim Fehlschlag las
+ man "beide Listen sind identisch (a, b, c)" und musste raten,
+ welche der beiden das war. */
+ const nurTafel = ausTafel.filter((r) => !ausWorkspace.includes(r));
+ const nurWorkspace = ausWorkspace.filter((r) => !ausTafel.includes(r));
+ ok(nurTafel.length === 0 && nurWorkspace.length === 0,
+ nurTafel.length === 0 && nurWorkspace.length === 0
+ ? `beide Listen sind identisch (${ausTafel.join(", ")})`
+ : `nur in rechte.js: [${nurTafel.join(", ")}] · `
+ + `nur in workspace.js: [${nurWorkspace.join(", ")}]`);
}
/* JEDE DATEI AUF DER PLATTE BRAUCHT EINEN EINTRAG.
diff --git a/server/rechte.js b/server/rechte.js
index bbea95b4..3719826e 100644
--- a/server/rechte.js
+++ b/server/rechte.js
@@ -67,6 +67,11 @@
* Rolle nur in einer der beiden Listen steht. */
export const ALLE_ROLLEN = [
"spicy", "admin", "manager", "scout", "creator", "hand", "modi",
+ /* DIE LINKE HAND (21.09.2026). Dieselbe Liste wie ROLLEN in
+ workspace.js -- pruef-rechtetafel haelt beide gegeneinander und
+ wird rot, sobald eine Rolle nur in einer steht. Genau das ist
+ beim Bauen passiert und hat den Eintrag hier gefunden. */
+ "linke",
/* DIE COMMUNITY (11.09.2026). Zuschauer mit einem Zugang, die den
Treff sehen -- und sonst nichts. Sie steht hier NICHT in `ALLE`
(siehe unten): Wer neu dazukommt, bekommt genau die zwei Seiten,
@@ -455,6 +460,63 @@ export const SEITEN = {
"/workspace/rechte.html": ["admin", "hand"],
};
+/* =====================================================================
+ DIE LINKE HAND BEKOMMT DIE SEITEN DER RECHTEN -- ABGELEITET
+ ===================================================================== (21.09.2026)
+
+ Filipe: *"Die linke Hand erhaelt grundsaetzlich dieselben Rechte wie
+ die rechte Hand in allen Bereichen, die die Modis, Aufgaben,
+ Aufgabenzuweisungen, Uebersichten, Kommunikation und Auswertungen
+ betreffen."*
+
+ WARUM ABGELEITET UND NICHT SIEBZEHNMAL "linke" HINGESCHRIEBEN: Die
+ naechste Seite, die jemand der rechten Hand gibt, bekaeme die linke
+ sonst nicht -- und das faellt niemandem auf, weil eine fehlende
+ Seite aussieht wie eine Seite, die es nicht gibt. Diese Schleife
+ kann das nicht vergessen.
+
+ DIE AUSNAHMEN STEHEN EINZELN DA, MIT GRUND. Eine Ausnahme ohne
+ Begruendung ist beim naechsten Umbau eine, die jemand fuer ein
+ Versehen haelt und wegnimmt. */
+const LINKE_NICHT = {
+ /* Personen anlegen -- ausdruecklich im Auftrag ausgeschlossen. Die
+ LISTE der Leute braucht sie trotzdem, um Aufgaben zu verteilen;
+ die kommt aus /api/personen/liste und ist dort eigens geoeffnet. */
+ "/workspace/personen.html": "legt Personen an",
+ /* Der Weg, auf dem neue Leute ueberhaupt hereinkommen. */
+ "/workspace/bewerbungen.html": "fuehrt zu neuen Personen",
+ "/workspace/talente.html": "fuehrt zu neuen Personen",
+ /* Der vertrauliche Meldeweg. Das Empfindlichste im Haus -- Meldungen
+ ueber Menschen. Im Zweifel das kleinere Recht: Ein Wort von Filipe
+ oeffnet es, eine versehentlich gelesene Meldung laesst sich nicht
+ zurueckholen. Ein Modi darf ihn uebrigens auch nicht. */
+ "/workspace/hilfe.html": "vertraulicher Meldeweg",
+ /* Wer die Rechtetafel bedient, verwaltet Konten -- nicht Aufgaben. */
+ "/workspace/rechte.html": "Rechteverwaltung",
+};
+
+for (const [seite, rollen] of Object.entries(SEITEN)) {
+ if (!Array.isArray(rollen) || !rollen.includes("hand")) continue;
+ if (LINKE_NICHT[seite]) continue;
+ if (rollen.includes("linke")) continue;
+ /* EINE KOPIE, KEINE AENDERUNG AM ORIGINAL. Mehrere Seiten teilen
+ sich dieselbe Liste (`ALLE`). Wer die an Ort und Stelle aendert,
+ gibt die Rolle allen Seiten, die dieselbe Liste benutzen -- auch
+ denen, die gerade eine Ausnahme sind. Das faellt nicht auf,
+ solange keine Ausnahme `ALLE` benutzt, und knallt an dem Tag, an
+ dem eine es tut.
+ Direkt hinter die rechte Hand -- die Reihenfolge in diesen Listen
+ ist an einigen Stellen die Anzeigereihenfolge. */
+ const kopie = rollen.slice();
+ kopie.splice(kopie.indexOf("hand") + 1, 0, "linke");
+ SEITEN[seite] = kopie;
+}
+
+/** Welche Seiten der rechten Hand die linke NICHT bekommt -- und warum.
+ * Exportiert, damit die Pruefung die Begruendung mitlesen kann statt
+ * eine eigene Liste zu fuehren. */
+export const LINKE_AUSNAHMEN = LINKE_NICHT;
+
/* Die Zugangswaende. Sie haben keine Rolle, weil sich dort noch
niemand angemeldet hat -- sie sind der Ort, an dem das passiert.
Sie stehen hier nur, damit die Pruefung "jede Datei hat einen
diff --git a/server/workspace-aufgaben.js b/server/workspace-aufgaben.js
index f17c82e8..9829ae75 100644
--- a/server/workspace-aufgaben.js
+++ b/server/workspace-aufgaben.js
@@ -184,6 +184,8 @@ function sichtbarRoh(person) {
gewollt und der kuerzeste ehrliche Weg, "diese beiden gleich" zu
sagen. */
case "hand":
+ /* Die linke Hand sieht dasselbe wie die rechte (21.09.2026). */
+ case "linke":
case "modi": {
const liste = [...TEAM_DOGI_ROLLEN].map((r) => `'${r}'`).join(", ");
const modis = db().prepare(
diff --git a/server/workspace-checkliste.js b/server/workspace-checkliste.js
index 48a15ab8..5fa8e3f3 100644
--- a/server/workspace-checkliste.js
+++ b/server/workspace-checkliste.js
@@ -36,7 +36,7 @@ import express from "express";
import {
db, sitzungLesen, istLeitung, istDogFather, siehtAlles, darfCreator, betreuteIds,
protokolliere, echteIp,
- siehtModis, TEAM_DOGI_ROLLEN,
+ siehtModis, TEAM_DOGI_ROLLEN, istHand,
} from "./workspace.js";
import { MODI_PUNKTE, MODI_GRUPPENTEXTE } from "./workspace-modi-punkte.js";
import {
@@ -367,7 +367,7 @@ checklisteRouter.get("/workspace/api/checkliste/:bereich", (req, res) => {
koennen. Bewerten darf sie darin genauso wenig wie DogFather;
das entscheidet `darf_bewerten` weiter unten und nicht diese
Liste. Sehen und Druecken sind zwei Fragen. */
- if (req.person.rolle === "admin" || req.person.rolle === "hand") {
+ if (req.person.rolle === "admin" || istHand(req.person)) {
for (const z of db().prepare(
"SELECT id FROM personen WHERE rolle = 'modi' AND aktiv = 1").all()) ids.push(z.id);
}
@@ -433,7 +433,7 @@ checklisteRouter.get("/workspace/api/checkliste/:bereich", (req, res) => {
ist schlicht falsch beschriftet. Das Wort kommt vom Server:
Ein Rollenvergleich im Browser waere die Stelle, an der der
Rollenname in einer ausgelieferten Datei landet. */
- wahl_name: req.person.rolle === "admin" || req.person.rolle === "hand"
+ wahl_name: req.person.rolle === "admin" || istHand(req.person)
? "Person" : "Creator",
/* Die Oberflaeche soll nicht raten muessen, wer was darf -- sonst
zeigt sie einem Creator einen Knopf, der beim Klicken 403 sagt.
diff --git a/server/workspace-entwicklung.js b/server/workspace-entwicklung.js
index 05c90e7c..ba0e9969 100644
--- a/server/workspace-entwicklung.js
+++ b/server/workspace-entwicklung.js
@@ -107,7 +107,7 @@ export const VERBLASST_NACH_TAGEN = 90;
von Dogi -- nicht die Agentur, nicht die Community. Als Liste und
nicht als "alle ausser X": Eine neue Rolle soll hier NICHT
automatisch eine Personalakte bekommen. */
-const KARTEN_ROLLEN = ["hand", "modi"];
+const KARTEN_ROLLEN = ["hand", "linke", "modi"];
/** Wer einen Neuen einarbeiten kann.
*
@@ -116,7 +116,7 @@ const KARTEN_ROLLEN = ["hand", "modi"];
* dieselbe Arbeit wie alle anderen. Ein Manager oder Scout dagegen
* arbeitet im anderen Haus und kann niemanden durch eine Schicht
* begleiten, die er selbst nicht macht. */
-const BUDDY_ROLLEN = ["admin", "hand", "modi"];
+const BUDDY_ROLLEN = ["admin", "hand", "linke", "modi"];
/** Welchen Zugang der letzte Schritt des Trichters anlegt.
*
diff --git a/server/workspace-personen.js b/server/workspace-personen.js
index c6ffcd2c..e5ed89f7 100644
--- a/server/workspace-personen.js
+++ b/server/workspace-personen.js
@@ -19,7 +19,7 @@ import express from "express";
naechsten Umzug stehen bleibt. */
import { anmeldeAdresseFuer } from "./crew-adresse.js";
import {
- db, protokolliere, echteIp, sitzungLesen, personAnlegen, codeNeu, sitzungToken, personSperren, betreuungSetzen, scoutZuteilungSetzen, istLeitung, istDogFather, siehtModis, siehtAlles, ROLLEN_SORTIERUNG, ROLLEN_REIHE, istSpicy, verborgeneIds, TEAM_DOGI_ROLLEN, darfAnlegen, darfRollenWechseln, darfRolleAendern, rollenZumAendern, hausBedingung,
+ db, protokolliere, echteIp, sitzungLesen, personAnlegen, codeNeu, sitzungToken, personSperren, betreuungSetzen, scoutZuteilungSetzen, istLeitung, istDogFather, siehtModis, siehtAlles, ROLLEN_SORTIERUNG, ROLLEN_REIHE, istSpicy, verborgeneIds, TEAM_DOGI_ROLLEN, darfAnlegen, darfRollenWechseln, darfRolleAendern, rollenZumAendern, hausBedingung, istHand,
} from "./workspace.js";
import { sicherungJetzt } from "./workspace-sicherung.js";
@@ -99,7 +99,10 @@ function nurAdmin(req, res, next) {
WELCHE Menschen sie zu sehen bekommt, entscheidet sie nicht: Auf
ihrer Adresse grenzt `hausBedingung` die Liste ohnehin auf Team
Dogi ein. */
- if (person.rolle === "hand" && nurListe) {
+ /* AUCH DIE LINKE HAND -- ABER NUR DIE LISTE (21.09.2026).
+ Sie muss sehen, wem sie eine Aufgabe geben kann. Anlegen darf sie
+ niemanden; das entscheidet nicht diese Zeile, sondern ANLEGBAR. */
+ if (istHand(person) && nurListe) {
req.person = person;
return next();
}
@@ -381,6 +384,25 @@ personenRouter.get("/workspace/api/verwaltung/personen", (req, res) => {
text: "Vertritt dich im Alltag und koordiniert das Team. Sieht "
+ "dieselben Aufgaben, Ideen und Angebote wie du – aber keine "
+ "privaten Kalender und keine Einzelchats.",
+ }, {
+ /* DIE LINKE HAND (21.09.2026). Filipe: "Die linke Hand
+ erhaelt grundsaetzlich dieselben Rechte wie die rechte Hand
+ in allen Bereichen, die die Modis, Aufgaben,
+ Aufgabenzuweisungen, Uebersichten, Kommunikation und
+ Auswertungen betreffen."
+
+ DERSELBE HUSKY wie bei der rechten Hand -- sie tun
+ dasselbe, und ein eigenes Zeichen wuerde einen Unterschied
+ behaupten, den es im Alltag nicht gibt. Was sie
+ unterscheidet, steht im Text darunter, und zwar
+ vollstaendig: Wer nur "wie die rechte Hand" liest, legt
+ sie an und wundert sich spaeter. */
+ wert: "linke", name: "Linke Hand", symbol: "husky",
+ text: "Dieselben Team- und Aufgabenrechte wie die rechte Hand – "
+ + "verteilt Aufgaben, sieht alle Auswertungen und den ganzen "
+ + "Treff. Kann aber keine Personen anlegen, keine Rollen "
+ + "ändern und sieht weder Bewerbungen noch den vertraulichen "
+ + "Meldeweg.",
}]
: []),
].sort((a, b) => {
diff --git a/server/workspace-treff.js b/server/workspace-treff.js
index dd088d17..ec3ad7af 100644
--- a/server/workspace-treff.js
+++ b/server/workspace-treff.js
@@ -74,7 +74,7 @@
import express from "express";
import {
db, sitzungLesen, protokolliere, echteIp,
- TREFF_BRETTER, TREFF_ROLLEN, ROLLEN_NAME,
+ TREFF_BRETTER, TREFF_ROLLEN, ROLLEN_NAME, istHand,
} from "./workspace.js";
/* Die Tabellen und die Sperrfrage stehen in einem Blatt ohne eigene
Importe -- workspace.js braucht beides ebenfalls, und ein Importkreis
@@ -96,7 +96,7 @@ const TAG = 86400000;
Treff. Genau dieser Fehler steckte am 11.09. schon einmal im stillen
Zugang.
------------------------------------------------------------------- */
-export const TREFF_TEAM_ROLLEN = new Set(["modi", "hand", "admin"]);
+export const TREFF_TEAM_ROLLEN = new Set(["modi", "hand", "linke", "admin"]);
const TREFF_TEAM = TREFF_TEAM_ROLLEN;
const TREFF_AUSSEN = new Set(["gast"]);
@@ -251,7 +251,7 @@ export function darfMassnahme(person, art, dauerhaft) {
/** Die längste Frist, die diese Rolle setzen darf (in Tagen). */
export const MODI_FRIST_TAGE = 7;
export function fristErlaubt(person, tage) {
- if (person?.rolle === "admin" || person?.rolle === "hand") return true;
+ if (person?.rolle === "admin" || istHand(person)) return true;
return tage > 0 && tage <= MODI_FRIST_TAGE;
}
diff --git a/server/workspace-treffchat.js b/server/workspace-treffchat.js
index 94c479d8..6d896900 100644
--- a/server/workspace-treffchat.js
+++ b/server/workspace-treffchat.js
@@ -98,7 +98,7 @@ export const TREFF_NAME = "Der Treff";
* ohne Moderation wäre nicht mutig, sondern fahrlässig — und die
* Nachtruhe ist kein Ersatz dafür, sie verkürzt nur die Zeit, in der
* jemand hinsehen muss. */
-export const TREFF_ROLLEN = ["gast", "modi", "hand", "admin"];
+export const TREFF_ROLLEN = ["gast", "modi", "hand", "linke", "admin"];
/** Ab wann und bis wann niemand schreibt — Ortszeit, Stunden.
*
diff --git a/server/workspace-werdegang.js b/server/workspace-werdegang.js
index 10d5560c..91128810 100644
--- a/server/workspace-werdegang.js
+++ b/server/workspace-werdegang.js
@@ -153,7 +153,7 @@ const TAG = 86400000;
* Import nur wegen zweier Zeichenketten die beiden Module
* aneinanderbindet; `pruef-werdegang` vergleicht sie und wird rot,
* wenn sie auseinanderlaufen. */
-export const KARTEN_ROLLEN = ["hand", "modi"];
+export const KARTEN_ROLLEN = ["hand", "linke", "modi"];
/** Wie viele Monatsspalten der Verlauf zeigt.
*
diff --git a/server/workspace.js b/server/workspace.js
index 42ebdf3a..7213cfc5 100644
--- a/server/workspace.js
+++ b/server/workspace.js
@@ -98,7 +98,44 @@ const SITZUNG_TAGE = 180;
const SITZUNG_MS = SITZUNG_TAGE * 86400_000;
const VERSUCHE_MAX = 8; // pro IP
const VERSUCHE_FENSTER_MIN = 10;
-const ROLLEN = new Set(["spicy", "admin", "manager", "scout", "creator", "hand", "modi", "gast"]);
+const ROLLEN = new Set(["spicy", "admin", "manager", "scout", "creator",
+ "hand", "linke", "modi", "gast"]);
+
+/* ===== DIE LINKE HAND (21.09.2026) ==================================
+
+ Filipe, aus dem Auftrag: *"Die linke Hand erhaelt grundsaetzlich
+ dieselben Rechte wie die rechte Hand in allen Bereichen, die die
+ Modis, Aufgaben, Aufgabenzuweisungen, Uebersichten, Kommunikation
+ und Auswertungen betreffen. Die linke Hand darf keine neuen Personen
+ oder Accounts hinzufuegen. Die linke Hand darf keine privaten bzw.
+ persoenlichen Bereiche oder Informationen von Dogfather sehen."*
+
+ DESHALB EINE MENGE UND NICHT DREISSIG ABSCHRIFTEN. Die Rolle "hand"
+ steht an 35 Stellen im Servercode. Haette ich ueberall ein "linke"
+ danebengeschrieben, waere die naechste Stelle, die jemand
+ hinzufuegt, die erste, die es vergisst -- und eine vergessene Stelle
+ heisst hier: Die linke Hand sieht etwas nicht, ohne dass jemand
+ merkt, warum. Genau diese Falle hat das Haus am 11.09. schon einmal
+ Datenspalten gekostet.
+
+ WAS AUSDRUECKLICH NICHT DAZUGEHOERT -- und warum jedes einzeln:
+
+ - Personen anlegen (ANLEGBAR) ausdruecklich im Auftrag
+ - Bewerbungen lesen das ist der Weg, auf dem
+ Personen hinzukommen
+ - Rollen aendern Kontoverwaltung, nicht
+ Aufgabenverwaltung
+ - der vertrauliche Meldeweg das Empfindlichste im Haus;
+ im Zweifel das kleinere Recht.
+ Ein Wort von Filipe kehrt das
+ um -- eine versehentlich
+ gelesene Meldung nicht.
+
+ Die Menge steht hier oben, VOR allem, was sie benutzt. */
+export const WIE_RECHTE_HAND = new Set(["hand", "linke"]);
+
+/** Hat diese Person die Team-Rechte einer Hand (rechts oder links)? */
+export const istHand = (person) => !!person && WIE_RECHTE_HAND.has(person.rolle);
/* DIE EINE REIHENFOLGE, IN DER ROLLEN UEBERALL ERSCHEINEN.
@@ -125,7 +162,8 @@ const ROLLEN = new Set(["spicy", "admin", "manager", "scout", "creator", "hand",
'gast' (Community) steht mit Absicht NICHT in der Liste: Sie faellt
dadurch ans Ende (siehe ELSE unten), und das ist richtig -- die
Community ist keine Stufe im Team. */
-export const ROLLEN_REIHE = ["spicy", "admin", "hand", "manager", "scout", "creator", "modi"];
+export const ROLLEN_REIHE = ["spicy", "admin", "hand", "linke", "manager",
+ "scout", "creator", "modi"];
/* DIE ROLLEN MIT EINER KACHEL AUF DER ANMELDESEITE.
@@ -168,7 +206,8 @@ const ROLLEN_KACHEL = new Set(["spicy", "admin", "manager", "scout", "creator"])
* 10.09. waere wieder umgeworfen worden. Er hat sich fuer die vierte
* Kachel entschieden; der Preis ist damit bewusst bezahlt und nicht
* uebersehen. */
-const CREW_KACHEL = new Set(["admin", "hand", "modi", "gast"]);
+/* Die linke Hand meldet sich ueber dieselbe Wand an wie die rechte. */
+const CREW_KACHEL = new Set(["admin", "hand", "linke", "modi", "gast"]);
/** Die Rollen mit einer Kachel auf der Zugangswand von treff.
*
@@ -231,7 +270,7 @@ export const istDogFather = (person) => !!person && person.rolle === "admin";
spicy und manager bleiben drin: Auf der Agenturseite verteilen sie
seit jeher Aufgaben, und daran soll sich nichts aendern. */
export const darfAufgabenVerteilen = (person) =>
- istLeitung(person) || person?.rolle === "hand";
+ istLeitung(person) || istHand(person);
/* ===== WER DARF EINE ROLLE AENDERN -- UND ZU WELCHER? (20.09.2026) ==
@@ -396,6 +435,12 @@ const ANLEGBAR = {
spicy: ["spicy", "manager", "scout", "creator"],
/* Ein Manager stellt Creator ein, die er dann auch betreut. */
manager: ["creator"],
+ /* DIE LINKE HAND LEGT NIEMANDEN AN (21.09.2026) -- ausdruecklich im
+ Auftrag. Sie stuende auch ohne diese Zeile auf [] (darfAnlegen
+ nimmt `?? []`), aber dann saehe es aus wie vergessen. Eine
+ ausdrueckliche leere Liste ist eine Entscheidung, eine fehlende
+ Zeile ist eine Luecke. */
+ linke: [],
};
/** Welche Rollen darf diese Person anlegen? Immer ein Feld, nie null —
@@ -449,6 +494,11 @@ export function darfAnlegen(person) {
export const darfRollenWechseln = (person) =>
!!person && (person.rolle === "admin" || person.rolle === "spicy"
|| person.rolle === "hand");
+/* OHNE "linke" -- UND DAS IST ABSICHT, KEIN VERGESSEN (21.09.2026).
+ Eine Rolle zu aendern ist Kontoverwaltung. Der Auftrag sagt zur
+ linken Hand ausdruecklich "keine neuen Personen oder Accounts
+ hinzufuegen"; wer Rollen vergeben kann, kann sich selbst und andere
+ zu allem machen, was es gibt. Deshalb hier die kleinere Antwort. */
/* WER FUEHRT TEAM DOGI? (10.09.2026)
*
@@ -463,7 +513,7 @@ export const darfRollenWechseln = (person) =>
* vergisst. Der Eingang ruft ab jetzt hierher.
*/
export const fuehrtTeamDogi = (person) =>
- !!person && (person.rolle === "admin" || person.rolle === "hand");
+ !!person && (person.rolle === "admin" || istHand(person));
/* =======================================================================
SPICY MEDIA (07.09.2026)
@@ -650,6 +700,12 @@ export const ROLLEN_NAME = {
scout: "Scout",
creator: "Creator",
hand: "Rechte Hand",
+ /* DIE LINKE HAND (21.09.2026). Ohne diesen Eintrag heisst sie
+ ueberall dort, wo der Rollenname steht -- im Treff, an einem
+ Beitrag, in der Personenliste -- schlicht "linke". Gefunden hat
+ das pruef-rechtetafel, weil sie diese Tabelle gegen ROLLEN haelt
+ und rot wird, sobald eine Rolle nur in einer der beiden steht. */
+ linke: "Linke Hand",
modi: "Modi",
/* DIE COMMUNITY (11.09.2026). Der Name steht hier und NICHT in einer
Datei, die jeder herunterlaedt -- ausgeliefert wird er nur an den,
@@ -1316,7 +1372,7 @@ export const TREFF_BRETTER = [...new Set([
/* Wer den Treff ueberhaupt sehen darf: die Community selbst und das
Team von Dogi. Die Agentur ausdruecklich nicht -- Creator, Scouts und
Spicy Media haben mit den Zuschauern dieses Streams nichts zu tun. */
-export const TREFF_ROLLEN = new Set(["gast", "modi", "hand", "admin"]);
+export const TREFF_ROLLEN = new Set(["gast", "modi", "hand", "linke", "admin"]);
/* DER VERTRAULICHE MELDEWEG (19.09.2026).
@@ -1479,7 +1535,7 @@ const TREFF_MIT_EIGENEM = [...TREFF_BEREICHE, HILFE_KACHEL, STECKBRIEF_KACHEL_TR
export function bereicheFuer(person) {
if (person?.rolle === "modi") return MODI_MIT_TREFF;
- if (person?.rolle === "hand") return HAND_MIT_TREFF;
+ if (istHand(person)) return HAND_MIT_TREFF;
/* DIE COMMUNITY SIEHT DEN TREFF UND SONST NICHTS (11.09.2026) --
seit dem 19.09. plus den eigenen Steckbrief. */
if (person?.rolle === "gast") return TREFF_MIT_EIGENEM;
@@ -1865,7 +1921,7 @@ export function kategoriePersonen(person) {
/* Ein Modi arbeitet ausschliesslich an eigenen Aufgaben -- dort gilt
sie immer, unabhaengig davon, wen er eintraegt. */
if (person.rolle === "modi") return null;
- if (person.rolle !== "admin" && person.rolle !== "hand") return [];
+ if (person.rolle !== "admin" && !istHand(person)) return [];
try {
/* Auch die rechte Hand selbst: Sie traegt Aufgaben ein -- fuer
andere UND fuer sich. Waere ihre eigene Nummer nicht dabei,
@@ -1884,10 +1940,12 @@ export function kategoriePersonen(person) {
die anderen zu stellen: Sie haelt den Ueberblick, sie steht nicht
darueber. */
const HAND_ROLLENTEXT = "Rechte Hand · du hast alles im Blick und entscheidest mit";
+const LINKE_ROLLENTEXT = "Linke Hand · du fuehrst das Team durch den Tag";
export function rollentextFuer(person) {
if (person?.rolle === "modi") return MODI_ROLLENTEXT;
if (person?.rolle === "hand") return HAND_ROLLENTEXT;
+ if (person?.rolle === "linke") return LINKE_ROLLENTEXT;
/* DIE COMMUNITY HATTE NUR EIN WORT (nachgetragen 19.09.2026).
Bei jeder anderen Rolle steht unter dem Titel ein Satz. Beim
@@ -3417,6 +3475,20 @@ function umstellungen(d) {
Transaktion, Indizes zurueck. */
checkListeErweitern(d, "personen", "rolle", "gast",
["spicy", "admin", "manager", "scout", "creator", "modi", "hand", "gast"], jetztStempel);
+ /* DIE LINKE HAND (21.09.2026) -- UND SIE STEHT MIT ABSICHT GANZ
+ UNTEN, NICHT BEI DER RECHTEN HAND.
+
+ `checkListeErweitern` baut die Regel NEU aus der Liste, die man
+ ihm gibt. Stand mein Schritt weiter oben, nahm der Schritt fuer
+ "gast" darunter die Rolle wieder heraus -- seine Liste kennt sie
+ ja nicht. Die Kette ist kumulativ und deshalb von der Reihenfolge
+ abhaengig; wer hier etwas einschiebt, gehoert ans ENDE.
+
+ Ohne diesen Schritt laesst die Datenbank die Rolle nicht zu -- und
+ zwar erst beim ersten echten Anlegen, nicht beim Start. */
+ checkListeErweitern(d, "personen", "rolle", "linke",
+ ["spicy", "admin", "manager", "scout", "creator", "modi", "hand",
+ "gast", "linke"], jetztStempel);
/* DER BEREICH FUER DAS IDEEN-BOARD (10.09.2026, Kapitel 5.6).
@@ -6452,7 +6524,7 @@ export function protokollLesen(anzahl = 20) {
* -- und das sagt bei der Community ueberall Nein. Zwei verschiedene
* Fragen, zwei verschiedene Funktionen; wer sie in eine legt, kann
* spaeter nicht mehr sagen, welche gerade zugeschlagen hat. */
-const HAUS_TEAM_ROLLEN = new Set(["admin", "hand", "modi", "gast"]);
+const HAUS_TEAM_ROLLEN = new Set(["admin", "hand", "linke", "modi", "gast"]);
function nurHaus(ids, person) {
if (person?.haus !== "crew") return ids;
diff --git a/workspace/anruf-probe.html b/workspace/anruf-probe.html
index afe6f0d8..b9e87786 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`. -->
-
-
+
+
-
+